# Gemini Developer Platform - Full Documentation Corpus > Full markdown content of every documentation page. See https://developer.gemini.com/llms.txt for the link index. # Last Updated: 2026-09-09T17:33:13.572Z --- URL: https://developer.gemini.com/troubleshooting.md # Troubleshooting Please use our [Sandbox](/get-started/sandbox) environment to develop and test your code. If your [private API request](/authentication/api-key#private-api-invocation) is failing, turn on debug logging so you can capture: - the request method and URL, with credentials and sensitive query parameters redacted - non-secret request headers, with API-key, authorization, payload, and signature values removed - the HTTP status code of the response - the response body after removing account identifiers and other sensitive data Never share API secrets, private keys, signed payloads, authorization tokens, or full unredacted request and response data. Make sure that you are not sending the JSON as the `POST` body. If you receive a `400` error that you are missing required data, then copy the base64 encoded string in `X-GEMINI-PAYLOAD` to a base64 decoder such as [https://www.base64decode.org/](https://www.base64decode.org/) and decode it. Compare the decoded JSON to the documentation for the endpoint you are trying to access. If you are receiving a `429` response, then see [Rate Limits](/rate-limit). ## Support If you still have a problem, use the [Gemini Support request form](/contact) with the following information: 1. Which environment did you try to make the request in, production or sandbox? 1. What URL were you trying to hit? 1. What IP address did you make the request from? 1. What date and time (including time zone) did you try to make the request? Please include, after redacting secrets and personal data: - relevant non-secret HTTP request headers - the redacted JSON response from the server If you leave any of this information out, the response to your support request may be delayed. --- URL: https://developer.gemini.com/tools.md # SDKs & Tools Build with Gemini using official SDKs and developer tools. The [Gemini Developer Platform](https://github.com/gemini/developer-platform) repository also brings together an MCP server, API samples, and agent skills. ## TypeScript SDK The official TypeScript SDK for the Gemini API is published as `@gemini-markets/sdk`. It has two entry points — `/server` for Node.js, Bun, and Deno, and `/browser` for frontend apps and edge runtimes. ```bash npm install @gemini-markets/sdk ``` - [Quickstart](/tools/typescript-sdk/quickstart) — install, create a client, make your first request - [Authentication](/tools/typescript-sdk/authentication) — HMAC, OAuth, and browser PKCE - [WebSocket](/tools/typescript-sdk/websocket) — real-time streams and live order books - [Error handling](/tools/typescript-sdk/errors) — error classes, diagnostics, and safe logging - [Patterns & recipes](/tools/typescript-sdk/patterns) — pagination, timeouts, heartbeat, and advanced configuration - [API Reference](/tools/typescript-sdk/reference/overview) — all 106 operations across 10 REST namespaces, with code examples - [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — streams, methods, and wire format - [Deep dives](/tools/typescript-sdk/deep-dives/order-book) — order book reconstruction, RFQ, data types, request validation, sessions, transport ## Developer Platform Tools These tools are open source and work with the APIs documented here. Follow the setup instructions in the linked repository before using them with production credentials. | Tool | What it will help you do | | --- | --- | | [MCP server](https://github.com/gemini/developer-platform/tree/main/packages/mcp-server) | Connect Claude, ChatGPT, and other MCP clients to Gemini API tools. It includes public market-data tools and authenticated tools for orders, funds, accounts, margin, and staking. | | [API samples](https://github.com/gemini/developer-platform/tree/main/samples) | Run REST and WebSocket examples in TypeScript, Python, and Go. The samples include public and authenticated API usage. | | [Agent skills](https://github.com/gemini/developer-platform/tree/main/skills) | Add Gemini-focused skills to Claude Code, including an integration guide and terminal candlestick charts. | See the [developer-platform README](https://github.com/gemini/developer-platform#readme) for installation instructions, configuration, and examples. Use [demo environment credentials](/get-started/sandbox) when testing. ## API specifications Inspect machine-readable contracts and generate data models or client scaffolding through the canonical [API Specifications](/api-specifications) catalog. Generic generated clients do not implement Gemini's authenticated signing transport: private Prediction Markets requests encode the logical request payload in `X-GEMINI-PAYLOAD` and send an empty HTTP body. Use generated code alongside the [API Reference](/api-reference) for authentication, permissions, and product-specific behavior that schemas cannot fully express. ## Build with Gemini today - [Set up authentication](/authentication/api-key) - [Use the demo environment](/get-started/sandbox) - [Explore API specifications](/api-specifications) - [Build an agent with current APIs](/build/agent) --- URL: https://developer.gemini.com/roles.md # Roles Gemini uses role-based access control to enforce least-privilege permissions across API keys. Roles let you isolate responsibilities: - Grant trading capabilities to automated bots. - Restrict withdrawal permissions to treasury systems. - Limit monitoring and reporting tools to read-only access. Configure key roles in [API Settings](https://exchange.gemini.com/settings/api).
When you create an API key, Gemini assigns the Trader role by default.
If you call an endpoint without the required role, Gemini returns HTTP `403 Forbidden` with reason `MissingRole`: - `403` status - a JSON response body with: - `reason` set to `MissingRole` - `message` explaining the required role See [Error Codes](/error-codes#error-codes) for additional error response details. ## Administrator
The Administrator role is exclusive to Master API keys.
The Administrator role permits keys to: - Create accounts within the Master Group - View accounts within the Master Group ### Trader The Trader role permits keys to: - Check balances - Place and cancel orders - Check order status - View deposit addresses - View active orders - View trade history and volume - View accounts within the Master Group ### Fund Manager The Fund Manager role permits keys to: - Check balances - View deposit addresses - Request new cryptocurrency deposit addresses - Withdraw cryptocurrency funds - View accounts within the Master Group - Execute internal transfers between two accounts in the same Master group ## Auditor
The Auditor role is read-only and cannot be combined with other roles.
The Auditor role permits keys to: - Check balances - Check order status - View deposit and withdrawal history - View deposit addresses - View active orders - View trade volume - View past trades - View accounts within the Master Group ### Endpoint summary The table below summarizes required roles by endpoint: #### Account Scoped Endpoints
Endpoint URI Trader can access? Fund Manager can access? Auditor can access?
[Create New Order](/rest/orders#create-new-order) `/v1/order/new`
[Cancel Order](/rest/orders#cancel-order) `/v1/order/cancel`
[Cancel All Session Orders](/rest/orders#cancel-all-session-orders) `/v1/order/cancel/session`
[Cancel All Active Orders](/rest/orders#cancel-all-active-orders) `/v1/order/cancel/all`
[Wrap Order](/rest/orders#wrap-order) `/v1/wrap/:symbol`
[Order Status](/rest/orders#get-order-status) `/v1/order/status`
[Get Active Orders](/rest/orders#list-active-orders) `/v1/orders`
[List Past Trades](/rest/orders#list-past-trades) `/v1/mytrades`
[Get Orders History](/rest/orders#list-past-orders) `/v1/orders/history`
[Get Trade Volume](/rest/orders#get-trading-volume) `/v1/tradevolume`
[Get Notional Volume](/rest/orders#get-notional-trading-volume) `/v1/notionalvolume`
[Heartbeat](/rest/session#heartbeat) `/v1/heartbeat`
[Get Available Balances](/rest/fund-management#get-available-balances) `/v1/balances`
[Get Notional Balances](/rest/fund-management#get-notional-balances) `v1/notionalbalances/:currency`
[Get Deposit Addresses](/rest/fund-management#list-deposit-addresses) `/v1/addresses/:network`
[New Deposit Address](/rest/fund-management#create-new-deposit-address) `/v1/deposit/:network/newAddress`
[Transfers](/rest/fund-management#list-past-transfers) `/v2/transfers`
[Custody Account Fees](/rest/fund-management#list-custody-fee-transfers) `/v1/custodyaccountfees`
[Withdraw Crypto Funds](/rest/fund-management#withdraw-crypto-funds) `/v2/withdraw/:network/:ticker`
[New Clearing Order](/rest/clearing#create-new-clearing-order) `/v1/clearing/new`
[Clearing Order Status](/rest/clearing#get-clearing-order) `/v1/clearing/status`
[Cancel Clearing Order](/rest/clearing#cancel-clearing-order) `/v1/clearing/cancel`
[Confirm Clearing Order](/rest/clearing#confirm-clearing-order) `/v1/clearing/confirm`
[Clearing Order List](/rest/clearing#list-clearing-orders) `/v1/clearing/list`
[Clearing Broker List](/rest/clearing#list-clearing-brokers) `/v1/clearing/broker/list`
[Clearing Trades](/rest/clearing#list-clearing-trades) `/v1/clearing/trades`
[Get Instant Quote](/rest/instant#get-instant-quote) `/v1/instant/quote`
[Execute Instant Order](/rest/instant#execute-instant-order) `/v1/instant/execute`
[Add A Bank](/rest/fund-management#add-bank) `/v1/payments/addbank`
[Add A Bank CAD](/rest/fund-management#add-bank-cad) `/v1/payments/addbank/cad`
[View Payment Methods](/rest/fund-management#list-payment-methods) `/v1/payments/methods`
[Account Detail](/rest/account-administration#get-account-detail) `/v1/account`
[List Approved Addresses](/rest/fund-management#list-approved-addresses) `/v1/approvedAddresses/account/:network`
[Create Approved Address](/rest/fund-management#create-new-approved-address) `/v1/approvedAddresses/:network/request`
[Remove Approved Address](/rest/fund-management#remove-approved-address) `/v1/approvedAddresses/:network/remove`
[FX Rate](/rest/market-data#fx-rate) `/v2/fxrate/:symbol/:timestamp`
#### Master Scoped Endpoints
Endpoint URI Administrator can access? Trader can access? Fund Manager can access? Auditor can access?
[Create Account](/rest/account-administration#create-new-account) `/v1/account/create`
[Rename Account](/rest/account-administration#rename-account) `/v1/account/rename`
[Get Accounts](/rest/account-administration#list-accounts-in-group) `/v1/account/list`
[Transfer Between Accounts](/rest/fund-management#transfer-between-accounts) `/v1/account/transfer/:currency`
[Transactions](/rest/fund-management#get-transaction-history) `/v1/transactions`
Master level API keys can access Account level endpoints if the proper role is assigned. The account will need to be passed in the payload of the request as detailed [Using Master API Keys](/account-admin-endpoints#using-master-api-keys).
--- URL: https://developer.gemini.com/rate-limit.md # Rate Limits To prevent abuse, Gemini imposes rate limits on incoming requests as described in the [Gemini API Agreement](https://www.gemini.com/legal/api-agreement). For public API entry points, we limit requests to 120 requests per minute, and recommend that you do not exceed 1 request per second. For private API entry points, we limit requests to 600 requests per minute, and recommend that you not exceed 5 requests per second. ### How are rate limits applied? When requests are received at a rate exceeding X requests per minute, we offer a "burst" rate of five additional requests that are queued but their processing is delayed until the request rate falls below the defined rate. When you exceed the rate limit for a group of endpoints, you will receive a `429` [Too Many Requests](https://www.webfx.com/web-development/glossary/http-status-codes/what-is-a-429-status-code/) HTTP status response until your request rate drops back under the required limit. **Example**: 600 requests per minute is ten requests per second, meaning one request every 0.1 second. If you send 20 requests in close succession over two seconds, then you could expect: - the first ten requests are processed - the next five requests are queued - the next five requests receive a 429 response, meaning the rate limit for this group of endpoints has been exceeded - any further incoming request immediately receive a 429 response - after a short period of inactivity, the five queued requests are processed - following that, incoming requests begin to be processed at the normal rate again --- URL: https://developer.gemini.com/privacy.md # Privacy for Gemini Developer Documentation # Privacy This documentation site publishes API references, examples, machine-readable specifications, and links to Gemini developer tools. Do not put API keys, private keys, signed request payloads, account identifiers, or other secrets into URLs, page feedback, issue reports, or example code. Use placeholders for credentials and redact sensitive response data before sharing diagnostics. The Gemini services and accounts described here are governed by the terms and privacy notices that apply to the relevant Gemini entity, product, and jurisdiction. Read the official [Gemini Privacy Policy](https://www.gemini.com/legal/privacy-policy) for information about personal-data processing, regional disclosures, and privacy contacts. For account or API questions, use [Gemini Support](https://support.gemini.com/hc/en-us/requests/new). This page is a documentation signpost, not a replacement for the official policy. If the documentation and a legal or privacy notice differ, the applicable official notice controls. --- URL: https://developer.gemini.com/platform.md # Gemini Developer Platform Gemini provides a unified developer platform to discover markets, manage accounts, and trade supported assets. > **Account model note:** Product access and account selection depend on your chosen interface. Always verify credential and account requirements for each product and protocol. ## Start with your trading product Choose what you want to trade before choosing a protocol. - [Spot crypto](/products/spot) — current trading guides and API documentation. - [Margin](/products/margin) — margin concepts and REST reference. - [Perpetuals](/products/perpetuals) — perpetuals concepts and derivatives REST reference. - [Prediction markets](/products/prediction-markets) — event-contract guides and API documentation. - **Stocks** — available in the Gemini UI. API trading documentation is coming soon. Instrument availability, interface coverage, and regional eligibility vary by product. Review product guides for specific rules. ## Understand credentials and accounts Credentials authenticate callers. Accounts define the execution context. Roles and OAuth scopes govern permitted operations, while product settings control asset access. Current REST documentation describes two API-key patterns: - A Master API key can target any subaccount in its group by passing the account shortname in supported requests. - An account-scoped API key is bound to one specific account. OAuth, WebSocket, and FIX have distinct authentication and account-selection rules. Always follow the documentation for your chosen interface. - [API key authentication](/authentication/api-key) - [OAuth](/authentication/oauth) - [Roles and permissions](/roles) - [Accounts and subaccounts](/rest-api/common/admin/subaccounts) ## Choose an interface - [REST APIs](/rest-api/rest-api) for request-and-response operations. - [WebSocket APIs](/websocket-api/websocket-api) for streaming data and real-time workflows. - [FIX APIs](/fix-api/fix-api) for institutional order routing and drop copies. - [SDKs & Tools](/tools) for the Gemini MCP server, code samples, and agent skills. SDK packages and the Gemini API CLI are in development. Use the [API Reference](/api-reference) for exact endpoint schemas and status codes. ## A safe integration flow 1. Choose a trading product and confirm its availability for your use case. 2. Create credentials supported by your interface. 3. Identify your target trading account. 4. Verify required product access and credential permissions. 5. Discover active instruments and review product-specific rules. 6. Preview operations when supported by the API. 7. Confirm the resolved account before executing mutating requests. 8. Monitor order status through WebSocket events or REST status endpoints. Test your implementation in the [demo environment](/get-started/sandbox) before trading live. --- URL: https://developer.gemini.com/gemini-clearing.md # Gemini Clearing Gemini Clearing lets two parties settle off-book trades directly. The initiator submits trade details for any supported [symbol](/market-data/symbols-and-minimums) and receives a `trade_id`. If you provide a `counterparty_id`, only that counterparty can confirm the trade. If you omit `counterparty_id`, any counterparty with the `trade_id` can settle the trade. --- URL: https://developer.gemini.com/error-codes.md # Error Codes When an API request fails, Gemini returns an HTTP 4xx or 5xx status code and a JSON response body detailing the failure. ## HTTP Error Codes | HTTP Status | Meaning | |-------------|---------| | 200 | Request succeeded | | 30x | API entry point moved. Check the `Location` header for redirect URL. | | 400 | Market not open, malformed request, or invalid authentication headers | | 403 | API key lacks the required role for this endpoint | | 404 | Unknown endpoint or order not found | | 406 | Insufficient funds | | 429 | [Rate limit](/rate-limit) exceeded | | 500 | Server encountered an error | | 502 | Technical issues prevented request fulfillment | | 503 | Exchange is down for maintenance | ## Error payload Failed requests return a non-200 HTTP status code and a JSON body containing three fields: 1. `result`: Always `"error"`. 2. `reason`: A short machine-readable error identifier from the table below. 3. `message`: A human-readable description of the error. | Reason | Meaning | |---|---| | ClientOrderIdTooLong | [Client Order ID](/client-order-id#client-order-id) must not exceed 100 characters | | ClientOrderIdMustBeString | [Client Order ID](/client-order-id#client-order-id) must be a string | | ConflictingOptions | Selected order execution options conflict with each other | | ConflictingAccountName | Specified name is already in use within the master group | | EndpointMismatch | Request path does not match the endpoint specified in payload | | EndpointNotFound | No endpoint specified | | GTSTradeIDMustBeString | [Clearing ID](/gemini-clearing#gemini-clearing) must be a string | | InsufficientFunds | Order rejected due to insufficient funds | | InvalidJson | Request body contains invalid JSON | | InvalidNonce | Nonce is not strictly greater than previous nonce or falls outside +/- 30 seconds of server epoch | | InvalidOrderType | Unsupported or unknown order type | | InvalidPrice | Invalid price specified for order | | InvalidStopPrice | Invalid stop price specified for stop-limit order | | InvalidStopPriceSell | Stop price for stop-limit sell order was below the sell price | | InvalidStopPriceBuy | Stop price for stop-limit buy order was above the buy price | | InvalidStopPriceRatio | Stop price deviates more than 50% from the limit price | | InvalidQuantity | Invalid or negative order quantity specified | | InvalidSide | Invalid order side specified (must be `buy` or `sell`) | | InvalidSignature | Request signature did not match payload and API secret | | InvalidSymbol | Unknown or invalid symbol | | InvalidTimestampInPayload | Payload contains an unsupported `timestamp` value | | InvalidAccountName | Account name does not match any account in the master group | | InvalidAccountType | Account type must be `exchange` or `custody` | | InvalidFundTransfer | Internal fund transfer failed | | Maintenance | Exchange is down for scheduled maintenance | | MarketNotOpen | Market is currently not accepting new orders | | MissingAccountName | Required account name omitted | | MissingAccounts | Required `account` field omitted | | MissingApikeyHeader | Missing `X-GEMINI-APIKEY` HTTP header | | MissingOrderField | Required `order_id` field omitted | | MissingRole | API key lacks the required role for this endpoint | | MissingPayloadHeader | Missing `X-GEMINI-PAYLOAD` HTTP header | | MissingPayloadKey | Payload is missing a required parameter | | MissingSignatureHeader | Missing `X-GEMINI-SIGNATURE` HTTP header | | MissingName | Required `name` field omitted | | MissingNonce | Missing `nonce` in payload. See [Private API Invocation](/authentication/api-key#private-api-invocation). | | MoreThanOneAccount | Multiple accounts supplied to a single-account endpoint | | AccountClosed | Account is closed and cannot perform this operation | | AccountsOnGroupOnlyApi | Account parameter supplied to a group-level endpoint using a non-master key | | AccountLimitExceeded | Number of accounts exceeds the endpoint limit | | NoAccountOfTypeRequired | Specified accounts do not match the required account type | | AccountNotOfTypeRequired | Specified account does not match the required account type | | NotGroupApiCompatible | Master API key used on an account-only endpoint | | ExceededMaxAccountsInGroup | Cannot create account because master group reached its maximum limit | | NoSSL | HTTPS is required for all API requests | | OptionsMustBeArray | Parameter `options` must be an array | | OrderNotFound | Specified order does not exist | | RateLimit | Request rate exceeded. See [Rate Limits](/rate-limit). | | System | Internal server error | | UnsupportedOption | Specified order execution option is not supported | | HasNotAgreedToCustodyTerms | Master group has not accepted Custody terms. Review and accept at https://exchange.gemini.com/custody. | | BadAccountType | Parameter `type` must be `exchange` or `custody` | | RemoteAddressForbidden | Request originated from an IP address not on the group allowlist | --- URL: https://developer.gemini.com/developers.md # Gemini Developer Platform # Gemini Developer Platform The Gemini Developer Platform is the official documentation hub for building integrations with Gemini Exchange. Use it when you need to discover a REST endpoint, stream market or account data over WebSocket, connect an institutional FIX workflow, or build an agent against Gemini's documented APIs. ## Choose a starting point - [Get started](/get-started/intro) to understand products, accounts, interfaces, and the demo environment. - [API Reference](/api-reference) to find operations by protocol and product. - [API Specifications](/api-specifications) to download the OpenAPI and AsyncAPI contracts. - [Authentication](/authentication/api-key) to create API keys and understand permissions and account context. - [API Settings](https://exchange.gemini.com/settings/api) to manage Gemini API credentials after signing in. - [OAuth](/authentication/oauth) to review OAuth flows and scopes where supported. - [SDKs & Tools](/tools) to use the open-source MCP server, API samples, and agent skills. ## Build safely Use the [demo environment](/get-started/sandbox) before sending production requests. Read the operation's authentication, role, scope, account, and product requirements before integrating. For an agent workflow, follow [Build an agent](/build/agent), fetch [llms.txt](/llms.txt) for the documentation index, and inspect [the specification catalog](/specs/index.json) before choosing an operation. Gemini's APIs expose market data, trading, account management, fund management, staking, perpetuals, and prediction-market capabilities where documented. Availability varies by product, account, jurisdiction, and permissions; the documentation for each operation is the source of truth. --- URL: https://developer.gemini.com/data-types.md # 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](http://json.org) 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 | A 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. | --- URL: https://developer.gemini.com/contact.md # Contact Gemini Developer Support # Contact and support Use the [Gemini Support request form](https://support.gemini.com/hc/en-us/requests/new) for account, API access, authentication, or production-service questions. Before opening a request, include the documentation URL, the relevant endpoint, the environment, the HTTP status, and a request identifier if returned. Never include API secrets, private keys, signed payloads, or personal credentials in a support request. For general Gemini customer support, use the official [Gemini Help Center](https://support.gemini.com). Send correspondence to Gemini Trust Company, LLC, 600 Third Avenue, 2nd Floor, New York, NY 10016. The published customer-support phone number is +1 (866) 240-5113. Gemini's primary support channel is the official support flow, so verify contact details against the Help Center before sharing account information. For documentation feedback or public examples, consult the [Gemini developer-platform repository](https://github.com/gemini/developer-platform). --- URL: https://developer.gemini.com/client-order-id.md # Client Order ID 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 [Create New Order](/rest/orders#create-new-order) endpoint. This makes it easy to track the [Order Events: Accepted](/websocket/archived/order-events#accepted) and [Order Events: Booked](/websocket/archived/order-events#booked) responses in your [Order Events](/websocket/streams#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,36}`. | 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 | --- URL: https://developer.gemini.com/api-specifications.md # API Specifications Use these machine-readable source specifications when generating clients, validating payloads, or giving agents a stable contract to inspect. ## Spec catalog - Spec catalog — JSON index of every published OpenAPI and AsyncAPI source file, including canonical URLs, compatibility aliases, versions, and related docs. ## Crypto Trading - Crypto Trading REST OpenAPI spec — source contract for Gemini Exchange REST APIs. Compatibility aliases: /rest.yaml and /api/openapi.yaml. Rendered reference: [Crypto Trading REST API reference](/rest). - Crypto Trading WebSocket AsyncAPI spec — source contract for the production `wss://ws.gemini.com` WebSocket request/response methods, market data streams, and authenticated streams. Compatibility alias: /websocket.yaml. ## Prediction Markets - Prediction Markets OpenAPI spec — source contract for Prediction Markets REST APIs. Compatibility alias: /prediction-markets.yaml. Rendered reference: [Prediction Markets API reference](/prediction-markets-spec). - Prediction Markets WebSocket AsyncAPI spec — shared WebSocket source contract covering Prediction Markets streams, authenticated streams, order methods, and combo RFQ messages. Compatibility alias: /websocket.yaml. ## WebSocket coverage | Surface | Machine-readable coverage | Notes | |---|---|---| | Request/response envelope | Full | Standard `id`, `method`, `params`, `status`, `result`, and `error` shapes are modeled. | | Utility methods (`conninfo`, `ping`, `time`) | Partial | Request envelopes are modeled; result bodies are broad because the docs do not enumerate every field. | | Subscription methods (`SUBSCRIBE`, `UNSUBSCRIBE`, `LIST_SUBSCRIPTIONS`) | Full | Uppercase Prediction Markets and lowercase Trading variants are both accepted in the spec. | | Market data streams | Full | Book ticker, partial depth, differential depth, and trade stream payloads are modeled. | | Trading methods | Partial | `order.place`, `order.cancel`, `order.cancel_all`, and `order.cancel_session` requests are modeled; order lifecycle state is modeled on the `orderUpdate` stream. | | Authenticated account streams | Full | Order, balance, and position stream payloads are modeled. | | Contract lifecycle stream | Full | `contractStatus` payloads are modeled. | | Combo RFQ streams and methods | Full | Public/private RFQ deliveries and quote submit/withdraw/confirm methods are modeled. | | Archived WebSocket v1/v2 APIs | Docs-only | Archived APIs remain documented for reference but are intentionally excluded from the current AsyncAPI contract. | ## Agent usage Agents should fetch /llms.txt first for the documentation index, then /specs/index.json for machine-readable API contracts. Use the catalog entry's `url` field as the canonical spec URL; root-level aliases remain available for existing integrations. For WebSocket coverage, read the AsyncAPI spec's `x-gemini-coverage` metadata before assuming a method response body is fully enumerated. --- URL: https://developer.gemini.com/api-reference.md # API Reference Use API Reference when you already know which technical interface you need. Start with a protocol, then choose the product and capability documented by that interface. ## Scope labels Start here. Core APIs provide the shared protocol, request shape, and behavior used across supported trading products. These pages document perpetuals-specific positions, margin, funding, and risk behavior. These pages document the few additions that apply only to Prediction Markets. An untagged reference may still have product or account requirements. Confirm the applicability stated on the operation or stream before integrating it. ## REST REST covers public market data and supported authenticated account and trading operations. Each operation page defines its request, authentication requirements, permissions, and response. - [REST API overview](/rest-api/rest-api) - [Trading REST API](/trading/rest-api/market-data) - [Margin REST API](/trading/rest-api/margin) - [Perpetuals REST API](/trading/rest-api/derivatives) - [Prediction Markets REST API](/rest-api/prediction-markets) - [Common administration REST API](/rest-api/common) ## WebSocket The WebSocket API has one core protocol. Connection, authentication, message envelopes, standard streams, and methods are shared across supported products. Prediction Market order events use the same order stream and add an outcome field. ### Core protocol - [Connection and session behavior](/websocket/introduction) - [Authentication](/websocket/authentication) - [Message format](/websocket/message-format) - [Shared market data and account streams](/websocket/streams) - [Position updates](/prediction-markets/websocket/streams#position-updates) - [Shared methods playground](/trading/websocket/playground) ### Prediction-market extensions - [Contract status](/prediction-markets/websocket/streams#contract-status) - [Combo RFQ streams](/prediction-markets/combos-rfq/websocket-streams) ## FIX Use the FIX reference for provisioned sessions, order entry, market data, drop copy, and message dictionaries. FIX session credentials and account mapping are interface-specific. - [FIX API overview](/fix-api/fix-api) ## Authentication and account context Before calling a private operation, verify all three separately: 1. The credential type is supported by the protocol and operation. 2. The credential has the required API-key role or OAuth scope. 3. The operation is resolved to the intended account using that interface's documented mechanism. - [API key authentication](/authentication/api-key) - [OAuth](/authentication/oauth) - [Roles and permissions](/roles) - [Accounts and subaccounts](/rest-api/common/admin/subaccounts) The reference remains organized under current public routes while canonical `/api-reference/...` operation URLs are introduced in later migration phases. --- URL: https://developer.gemini.com/account-admin-endpoints.md # Account Administration & Subaccounts Gemini subaccounts let institutional groups, trading desks, and automated agents manage segregated trading environments under a single group. Subaccounts operate as isolated containers with separate balances, order books, rate limits, and API keys. You can manage all subaccounts centrally with a single **Master API key** and internal transfers. --- ## The Gemini Account Hierarchy Gemini organizes accounts into a three-tier hierarchy: | Layer | Purpose | Scope & Lifecycle | |---|---|---| | **Account Group** | Top-level institutional container | Provisioned by Gemini during onboarding. Holds KYC/AML records, group-wide risk settings, and approved withdrawal whitelists. You cannot create or delete groups via API. | | **Account / Subaccount** | Individual trading or custody entity | Created dynamically via `POST /v1/account/create` or in [API Settings](https://exchange.gemini.com/settings/api). Holds balances, open orders, trades, and position history. Can be of type `exchange` or `custody`. | | **User & API Keys** | Identity & access controls | Users belong to one or more accounts. Access is granted via **Roles** assigned per user-account pair or per API key. | ``` ┌─────────────────────────────────────────────────────────┐ │ Account Group │ │ KYC Records · Approved Addresses · Group Settings │ └───────────────┬─────────────────┬───────────────────────┘ │ │ ┌──────────▼──────┐ ┌──────▼──────────┐ │ Subaccount A │ │ Subaccount B │ │ (exchange) │ │ (custody) │ └──────────┬──────┘ └──────┬──────────┘ │ │ └────────┬────────┘ │ Users & API Keys (Master or Account level) ``` --- ## Account Administration Endpoints The API provides administrative control over subaccount creation, listing, detail inspection, renaming, and internal fund transfers: | Endpoint | Method | Role Required | Description | |---|---|---|---| | [`/v1/account/create`](/rest-api/common/admin/create-new-account) | `POST` | Administrator | Creates a new subaccount under your group (`type: exchange` or `type: custody`). Returns the kebab-cased `account` shortname. | | [`/v1/account/list`](/rest-api/common/admin/list-accounts-in-group) | `POST` | Administrator / Auditor | Lists all subaccounts in your group, returning names, account IDs, shortnames, and creation timestamps (up to 500 per call). | | [`/v1/account`](/rest-api/common/admin/get-account-detail) | `POST` | Administrator / Auditor | Retrieves detailed information for a specific subaccount (users, roles, country codes, and status). | | [`/v1/account/rename`](/rest-api/common/admin/rename-account) | `POST` | Administrator | Renames a subaccount display name or kebab-cased shortname. | | [`/v1/account/transfer/{currency}`](/trading/rest-api/fund-management/transfer-between-accounts) | `POST` | Fund Manager | Executes instant zero-fee transfers between two subaccounts in the same group. | --- ## Using Master API Keys Gemini supports **Master API Keys** (prefixed with `master-`) and **Account-Level API Keys** (prefixed with `account-`). - **Master API Keys**: Created at the Account Group level in [API Settings](https://exchange.gemini.com/settings/api). Master keys can invoke account-level endpoints across any subaccount in the group if assigned the appropriate [roles](/roles#roles). - **Targeting Subaccounts**: To target a specific subaccount using a Master API key, pass the subaccount kebab-cased shortname in the `"account"` request parameter: ```json { "request": "/v1/balances", "nonce": 1776294447000, "account": "primary-trading" } ``` > [!NOTE] > If a Master key request omits the `"account"` parameter, the request defaults to the primary account to which the key was initially attached. Account-level keys (`account-...`) are strictly locked to their single assigned account and cannot pass the `"account"` parameter. --- ## Subaccount Architectural Patterns Subaccounts provide clean boundaries for risk containment, strategy isolation, team permissions, and bot operations: ### 1. Agentic & Automated Trading Bots Assign each trading bot or LLM agent its own dedicated subaccount and an **account-level API key** restricted to the `Trader` role. - **Blast Radius**: A bug or runaway strategy in one bot cannot deplete funds in other accounts. - **Rate Limits & Order Scope**: Rate limits are enforced independently per subaccount. Executing [Cancel All Orders](/trading/rest-api/orders/cancel-all-active-orders) cancels orders exclusively within that bot's subaccount. - **Orchestrator Control**: The central orchestrator holds the Master API key with `Administrator` and `Fund Manager` roles to provision and fund subaccounts. ``` Group ── agent-trend (exchange) ← Account Key (Trader role) ├─ agent-arb (exchange) ← Account Key (Trader role) └─ agent-mm (exchange) ← Account Key (Trader role) Master Key held by Control Plane (Administrator + Fund Manager roles) ``` ### 2. Prediction Market Bots Organize prediction market trading bots into separate subaccounts by market category (e.g., `pm-crypto`, `pm-sports`, `pm-weather`). - **Group Terms Acceptance**: Accept prediction markets terms once at the group level (`POST /v1/prediction-markets/terms/accept`); all subaccounts under the group inherit trading access. - **Isolated Settlements**: Any unexpected loss or price index movement on contract settlement is bounded strictly to that bot's subaccount. ### 3. Prop vs. Client Funds Keep firm capital and client assets completely segregated: - Place client cold-storage funds in `custody` subaccounts. - Run active proprietary trading strategies in `exchange` subaccounts. - Use internal transfers (`POST /v1/account/transfer/{currency}`) to rebalance capital without incurring network gas or on-chain withdrawal fees. --- ## End-to-End Subaccount Workflow 1. **Provision Master Key**: Create a Master API key with `Administrator` + `Fund Manager` roles in [API Settings](https://exchange.gemini.com/settings/api). 2. **Create Subaccount**: Call [`POST /v1/account/create`](/rest-api/common/admin/create-new-account) with `name: "My Bot"` and `type: "exchange"`. Record the returned `account` shortname (e.g., `my-bot`). 3. **Fund Subaccount**: Call [`POST /v1/account/transfer/{currency}`](/trading/rest-api/fund-management/transfer-between-accounts) to move funds from your main account to `my-bot`. 4. **Operate & Trade**: Issue orders using the Master key with `"account": "my-bot"`, or issue a dedicated account key scoped to `my-bot`. 5. **Reconcile**: Call [`POST /v1/account/list`](/rest-api/common/admin/list-accounts-in-group) or [`POST /v1/balances`](/trading/rest-api/fund-management/get-available-balances) across subaccounts. --- ## Constraints & Requirements - **Prerequisite**: Subaccounts and Master API keys require an institutional account group provisioned by Gemini onboarding. - **Shortname Usage**: Always use kebab-cased shortnames (e.g., `trading-desk-1`), not human display names, in the `"account"` payload parameter. - **Transfers**: Internal transfers (`POST /v1/account/transfer/{currency}`) can only move funds between subaccounts within the **same** Account Group. - **Roles per Key**: Master keys must be granted the required role for the action performed (`Administrator` for creation/listing, `Fund Manager` for transfers, `Trader` for order placement). See [Roles](/roles#roles). --- ## Related Documentation - [Subaccounts Overview](/rest-api/common/admin/subaccounts) — In-depth architectural guide & isolation patterns - [Master API Keys](/authentication/api-key#subaccount-operations-master-api-keys) — API key signing mechanics & headers - [Roles & Permissions](/roles#roles) — Role matrix across Master and Account endpoints - [Create New Account](/rest-api/common/admin/create-new-account) · [List Accounts](/rest-api/common/admin/list-accounts-in-group) · [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts) --- URL: https://developer.gemini.com/about.md # About Gemini Developer Platform # About Gemini Developer Platform Gemini Developer Platform is the official technical documentation site for Gemini Exchange APIs and developer tools. It explains how to discover market data, build trading and account workflows, work with Spot, Margin, Perpetuals, Staking, and Prediction Markets products, and connect through REST, WebSocket, or FIX where supported. This site is written for developers, institutions, and agents that need a precise integration reference. Start with the [Developer Platform overview](/developers) and use the [API Reference](/api-reference) for operation details. Inspect the [machine-readable specifications](/api-specifications) when generating clients or validating payloads. Product availability, account access, permissions, and jurisdictional restrictions can vary, so follow the requirements on each operation before making a request. For information about Gemini as a company and its consumer and institutional services, visit [gemini.com/about](https://www.gemini.com/about). --- URL: https://developer.gemini.com/websocket-api/websocket-api.md # WebSocket APIs --- URL: https://developer.gemini.com/websocket/streams.md # Stream Reference Book, depth, trade, order, and balance streams share this reference across supported products. The Gemini WebSocket API provides real-time market data and account event streams: - L2 Order Book depth and tickers - Trade events and candles - Account-level orders and balance updates ## Book Ticker | Schema | Frequency | Description | |----|----|----| | `{symbol}@bookTicker` | Real-time | Real time updates to the best bid/ask price for an order book. | ```json { "u": 1751505576085, "E": 1751508438600117161, "s": "btcusd", "b": "45000.50", "B": "1.25000000", "a": "45001.00", "A": "0.75000000" } ``` | Field | Type | Description | |-------|--------|--------------------------| | `u` | number | Update ID | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `b` | string | Best bid price | | `B` | string | Best bid quantity | | `a` | string | Best ask price | | `A` | string | Best ask quantity | | `c` | string | Last trade price (present once the book has traded; omitted otherwise) | | `C` | string | Last trade size (present once the book has traded; omitted otherwise) | --- ## L2 Partial Depth Streams | Schema | Frequency | Description | |----|----|----| | `{symbol}@depth5` | Periodic (1s) | Periodic snapshot of the top 5 levels once per second | | `{symbol}@depth10` | Periodic (1s) | Top 10 levels | | `{symbol}@depth20` | Periodic (1s) | Top 20 levels | | `{symbol}@depth5@100ms` | Periodic (100ms) | Top 5 levels every 100 milliseconds | | `{symbol}@depth10@100ms` | Periodic (100ms) | Top 10 levels | | `{symbol}@depth20@100ms` | Periodic (100ms) | Top 20 levels | ```json { "lastUpdateId": 12345678, "bids": [ ["45000.50", "1.25000000"], ["45000.25", "0.50000000"] ], "asks": [ ["45001.00", "0.75000000"], ["45001.25", "2.00000000"] ] } ``` | Field | Type | Description | |----------------|----------|------------------------------| | `lastUpdateId` | number | Last update ID | | `bids` | array | Array of [price, quantity] | | `asks` | array | Array of [price, quantity] | --- ## L2 Differential Depth Streams | Schema | Frequency | Description | |----|----|----| | `{symbol}@depth` | Periodic (1s) | List of all changed price levels in the last second | | `{symbol}@depth@100ms` | Periodic (100ms) | In the last 100 milliseconds | :::tip[Initial Snapshot] Use the [`snapshot` connection parameter](/websocket/introduction#snapshot-parameter) to receive an initial orderbook snapshot when subscribing. Connect with `wss://ws.gemini.com?snapshot=-1` for a full snapshot, or specify a positive number for top N levels. ::: :::note Quantity zero indicates price level removal. ::: ```json { "e": "depthUpdate", "E": 1751508260659505382, "s": "btcusd", "U": 12345677, "u": 12345678, "b": [ ["45000.50", "1.25000000"], ["45000.25", "0.00000000"] ], "a": [ ["45001.00", "0.75000000"] ] } ``` | Field | Type | Description | |-------|--------|------------------------------------| | `e` | string | Event type ("depthUpdate") | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `U` | number | First update ID in this event | | `u` | number | Last update ID in this event | | `b` | array | Bid updates [price, quantity] | | `a` | array | Ask updates [price, quantity] | --- ## Trade Stream | Schema | Frequency | Description | |----|----|----| | `{symbol}@trade` | Real-time | Real time trade executions | ```json { "E": 1759873803503023900, "s": "btcusd", "t": 2840140956529623, "p": "120649.97000", "q": "0.0046190900", "m": true } ``` | Field | Type | Description | |-------|---------|-------------------------| | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `t` | number | Trade ID | | `p` | string | Price | | `q` | string | Quantity | | `m` | boolean | Is buyer the maker | --- ## Order Events :::warning Requires an authenticated session ::: | Schema | Frequency | Description | |----|----|----| | `orders@account` | Real-time | Real time order activity for the account associated with the authenticated API key | | `orders@session` | Real-time | Real time order activity for the authenticated API key | ```json # Order Event - New { "e":"orderUpdate", "E":1759291847686856569, "s":"BTCUSD", "i":73797746498585286, "c":"my-order-1759291847503", "S":"BUY", "o":"LIMIT", "X":"NEW", "p":"1.00000", "q":"0.0000100000", "z":"0.0000100000", "T":1759291847686856569 } # Order Event - Canceled { "e":"orderUpdate", "E":1759291847731455006, "s":"BTCUSD", "i":73797746498585286, "c":"my-order-1759291847503", "X":"CANCELED", "T":1759291847731455006 } ``` | Field | Type | Description | |-------|---------|-------------------------| | `e` | string | Event type (`orderUpdate`) | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `i` | number | Order ID | | `c` | string | Client order ID. For RFQ maker fills, this is the `clientId` supplied to `rfq.submit_quote`, or Gemini's deterministic RFQ client order ID when omitted. | | `S` | string | Side, `BUY / SELL` | | `o` | string | Type, `LIMIT / MARKET / STOP_LIMIT / STOP_MARKET` | | `X` | string | Status, `NEW / OPEN / FILLED / PARTIALLY_FILLED / CANCELED / REJECTED / MODIFIED` | | `p` | string | Order price | | `P` | string | Stop price (`0` when not a stop order) | | `q` | string | Original quantity | | `z` | string | Remaining quantity | | `Z` | string | Executed quantity. For `FILLED` / `PARTIALLY_FILLED` events, this is the quantity filled in the last execution. For `CANCELED` and other events, this is the cumulative quantity filled over the lifetime of the order. Use `Z` (not the order status) to determine how much filled — e.g. a fully-filled `IOC` terminates as `CANCELED`. | | `L` | string | Last execution price | | `t` | number | Trade ID | | `n` | string | Fee amount (only present in 'FILLED' events) | | `m` | boolean | Maker flag on fills: `true` = maker, `false` = taker (present on fills only) | | `r` | string | Rejection reason | | `T` | number | Update time (nanoseconds) | | `O` | string | Event outcome for event contracts, `YES / NO` | Prediction Market order events use this core stream and add the `O` event-outcome field. :::note Fields with empty or zero values may be omitted from the event. ::: :::note Post-only and immediate time-in-force orders are **accepted, then cancelled** — they are never `REJECTED`: - `MOC` (maker-or-cancel / post-only): if it would take liquidity, the order is cancelled with `MakerOrCancelWouldTake` and never fills. - `IOC` (immediate-or-cancel): fills whatever crosses immediately, then cancels the remainder with `ImmediateOrCancelWouldPost`. A fully-filled `IOC` still ends with a `CANCELED` event — **so determine what filled from the executed quantity (`Z`), never from the final order status.** - `FOK` (fill-or-kill): fills completely and immediately, or is cancelled in full with `FillOrKillWouldNotFill` (no partial fills). `order.place` for these still returns a `200` response with an initial `NEW`; a true rejection returns a non-`200` status with an error code. ::: #### Rejection Reasons When an order is `REJECTED`, the `r` field contains one of: | Reason | Description | |--------|-------------| | `MarketNotOpen` | Market is closed or paused | | `InsufficientFunds` | Account lacks sufficient balance | | `InvalidPrice` | Price violates constraints | | `LimitPriceOffTick` | Price does not align with tick size | | `InvalidQuantity` | Quantity below minimum or off increment | | `InvalidStopPrice` | Stop price violates constraints | | `InvalidTotalSpend` | Total spend calculation error | | `DuplicateOrder` | Duplicate client order ID | | `InsufficientLiquidity` | Not enough liquidity at price | | `UnknownInstrument` | Trading pair does not exist | #### Cancellation Reasons When an order is `CANCELED` by the system, the `r` field contains one of: | Reason | Description | |--------|-------------| | `SelfCrossPrevented` | Self-trade prevention triggered | | `FillOrKillWouldNotFill` | FOK order could not fill completely | | `ImmediateOrCancelWouldPost` | IOC order would post to book | | `MakerOrCancelWouldTake` | MOC order would take liquidity | | `AuctionCancelled` | Auction-related cancellation | | `ExceedsPriceLimits` | Price moved beyond limits | --- ## Balance Updates :::warning Requires an authenticated session ::: | Schema | Frequency | Description | |----|----|----| | `balances@account` | Real-time | Real time balance updates for the account associated with the authenticated API key | | `balances@account@1s` | Periodic (1s) | Periodic snapshot of all balances every second for the account associated with the authenticated API key | The `balances@account` stream pushes updates in real time whenever a balance change occurs, and only includes the assets that changed. The `balances@account@1s` stream sends a complete snapshot of all account balances every second, regardless of whether they changed. On subscribe, `balances@account@1s` will immediately send the current balances if available. ```json # Balance Update { "e": "balanceUpdate", "E": 1768250434780000000, "u": 1768250421600000000, "B": [ { "a": "USD", "f": "207.39", "c": "207.39" } ] } ``` | Field | Type | Description | |-------|---------|-------------------------| | `e` | string | Event type ("balanceUpdate") | | `E` | number | Event time (nanoseconds) | | `u` | number | Time of the last account update (nanoseconds) | | `B` | array | Balance updates | | `a` | string | Asset code | | `f` | string | Available balance (amount available to trade) | | `c` | string | Confirmed balance (total balance including pending) | --- ## Contract Status Prediction-market contract lifecycle events — status transitions (e.g. `Awaiting Approval` → `Approved` → `Active`) and strike-populated moments for Up/Down contracts. | Schema | Frequency | Description | |----|----|----| | `contractStatus` | Real-time | Status changes and strike-price updates for prediction-market contracts | ```json # Strike-based contract (e.g. HI78999D63) { "e": "contractStatus", "E": 1776871540195, "s": "gemi-btc15m2604221545-hi78999d63", "k": "btc15m2604221545", "c": "HI78999D63", "i": 134794, "p": "78999.63", "o": "Awaiting Approval", "n": "Approved" } # Up/Down contract (no numeric strike — `p` omitted until populated) { "e": "contractStatus", "E": 1776871295498, "s": "gemi-btc05m2604221630-up", "k": "btc05m2604221630", "c": "UP", "i": 134791, "o": "Awaiting Approval", "n": "Approved" } ``` | Field | Type | Description | |-------|--------|-------------| | `e` | string | Event type (`contractStatus`) | | `E` | number | Event time (Unix milliseconds) | | `s` | string | Instrument symbol | | `k` | string | Event ticker | | `c` | string | Contract ticker (e.g. `HI78999D63`, `UP`, `DOWN`) | | `i` | number | Contract ID | | `p` | string | Strike price parsed from the contract ticker. Omitted for Up/Down contracts until the strike is set at activation | | `o` | string | Previous status | | `n` | string | New status | :::note For Up/Down contracts, `p` is omitted while the strike is unknown and included once it is set — subscribers can detect strike availability by the field's presence. ::: --- URL: https://developer.gemini.com/websocket/message-format.md # Message Format Our WebSocket API uses JSON-formatted messages for all communication. ### Request Format All requests follow a consistent structure: ```json { "id": "1", "method": "METHOD_NAME", "params": {...} } ``` | Field | Type | Required | Description | |----------|-------------------|----------|------------------------------------------------| | `id` | string \| number | Yes | Unique identifier for matching request/response | | `method` | string | Yes | The method to invoke | | `params` | object \| array | No | Method parameters (varies by method) | ### Response Format Successful responses include the request ID and result: ```json { "id": "1", "status": 200, "result": {...} } ``` | Field | Type | Description | |----------|------------------|------------------------------------------| | `id` | string \| number | Matches the request ID | | `status` | number | HTTP status code | | `result` | any | Method-specific response data | ### Error Response Error responses include error details: ```json { "id": "1", "status": 401, "error": { "code": -1002, "msg": "Authentication required" } } ``` | Field | Type | Description | |-----------------|------------------|---------------------------------| | `id` | string \| number | Matches the request ID | | `status` | number | HTTP status code | | `error.code` | number | Internal error code | | `error.msg` | string | Human-readable error message | ### Error Codes | Code | HTTP Status | Description | |--------|-------------|-------------------------------| | -1000 | 500 | Internal server error | | -1002 | 401 | Authentication required | | -1003 | 429 | Rate limit exceeded | | -1013 | 400 | Invalid parameters | | -1020 | 400 | Unsupported operation | | -2010 | 400 | Order rejected | ### Event Types Streaming events carry an `e` field that identifies the event type, so a single connection can demultiplex every subscription: | `e` value | Stream | |-----------|--------| | `depthUpdate` | L2 differential depth (`{symbol}@depth`, `{symbol}@depth@100ms`) | | `orderUpdate` | Order events (`orders@account`, `orders@session`) | | `balanceUpdate` | Balance updates (`balances@account`, `balances@account@1s`) | | `contractStatus` | Contract status (`contractStatus`) | The Book Ticker (`{symbol}@bookTicker`), L2 Partial Depth (`{symbol}@depth5` / `@depth10` / `@depth20`), and Trade (`{symbol}@trade`) payloads do **not** carry an `e` field — identify those by the stream you subscribed to. :::note New event types may be added over time. Treat any `e` value you do not recognize as a forward-compatible addition and ignore it. ::: --- URL: https://developer.gemini.com/websocket/introduction.md # Introduction **Version:** 0.10.7 • **Status:** Production • **Public URL:** `wss://ws.gemini.com` Our WebSocket API provides low latency access to real-time market data and order execution for professional traders and institutions. Built from the ground up for performance, our WebSocket API delivers fastest latency on AWS with enterprise-grade reliability. :::tip 🚀 [**Try It Now** with our interactive documentation](/websocket/playground#method-subscribe) ::: ### Key Features - **Low Latency** - Sub-10ms market data updates for competitive advantage - **Real-Time Trading** - Place and cancel orders via WebSocket - **Multiple Streams** - Subscribe to multiple markets simultaneously ### Performance Tiers | Tier | Target | Description | |------|---------------------|-------------| | **Tier 2** _(Public Internet)_ | p99~15ms | Public offering connecting to **AWS us-east-1 over the public internet**. Provides good **baseline performance** with minimal setup complexity. | | **Tier 1** _(In Region)_ | p99~10ms | **Direct connection** to us-east-1 feed. Provides **improved performance** a step above the public offering but requires onboarding to peer to our infrastructure. | | **Tier 0** _(Local Zone)_ | p99~5ms | **Best performance** outside of NY5, physically closest to our data center. Requires onboarding similar to us-east-1. | :::info Please email api@gemini.com to onboard to our WebSocket high performance tiers. ::: ### Connection Parameters Connection-level query parameters can be passed in the WebSocket URL to customize behavior: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `snapshot` | integer | `0` | Controls initial orderbook snapshot delivery when subscribing to differential depth streams | | `cancelOnDisconnect` | boolean | `false` | Automatically cancel all open orders when the WebSocket session disconnects | #### Snapshot Parameter The `snapshot` parameter controls whether an initial orderbook snapshot is sent when subscribing to [differential depth streams](/websocket/streams#l2-differential-depth-streams) (`{symbol}@depth`, `{symbol}@depth@100ms`). **Connection URL example:** ``` wss://ws.gemini.com?snapshot=-1 ``` | `snapshot` Value | Behavior | |------------------|----------| | Not specified / `0` | No snapshot — only incremental updates (default) | | `-1` (or any negative) | Full orderbook snapshot sent immediately on subscribe | | Positive integer (e.g., `100`) | Top N levels snapshot sent on subscribe | :::tip Use `snapshot=-1` to receive a full orderbook snapshot immediately when subscribing to differential depth streams. This is useful for initializing your local orderbook state without needing a separate REST API call. ::: #### Cancel On Disconnect Parameter The `cancelOnDisconnect` parameter enables automatic cancellation of all your open orders when the WebSocket session disconnects. This is a safety feature that helps prevent unwanted exposure from stale orders if your connection drops unexpectedly. **Connection URL example:** ``` wss://ws.gemini.com?cancelOnDisconnect=true ``` | `cancelOnDisconnect` Value | Behavior | |----------------------------|----------| | Not specified / `false` | Orders remain active after disconnect (default) | | `true` | All open orders are automatically cancelled on disconnect | :::warning When `cancelOnDisconnect=true` is enabled, **all** open orders placed via the WebSocket session will be cancelled upon disconnection, including orders that may have been intentionally left open. Ensure your trading strategy accounts for this behavior. ::: :::tip Combine multiple connection parameters using `&`: `wss://ws.gemini.com?snapshot=-1&cancelOnDisconnect=true` ::: ### Authentication Public market data streams are available without authentication. Authenticated streams (order events, trading) require either an HMAC-signed API key or an OAuth 2.0 bearer token passed on the WebSocket upgrade. See [Authentication](/websocket/authentication) for details. --- URL: https://developer.gemini.com/websocket/authentication.md # Authentication The WebSocket API accepts either an HMAC-signed API key or an OAuth 2.0 bearer token on the connection upgrade. Pick whichever fits your app. ## Generate an API Key API keys for our WebSocket API have special requirements: 1. Navigate to [API Settings](https://exchange.gemini.com/settings/api) 2. Click **"Create API key"** 3. **Scope:** Select the account you want to trade with 4. **Settings:** - ✅ Enable **"Uses a time-based nonce"** - ✅ Enable **"Trading"** 5. Save your API key and secret securely :::warning Only **account-scoped** keys with **time-based** nonces are accepted. ::: --- ## Create an Authenticated Connection Pass the following headers when establishing the websocket connection, | Header | Value | |--------------------|---------------------------------------------------------| | `X-GEMINI-APIKEY` | Your Gemini API key (session key) | | `X-GEMINI-NONCE` | Current epoch timestamp in seconds | | `X-GEMINI-SIGNATURE` | `hex(HMAC_SHA384(base64(nonce), key=api_secret))` | | `X-GEMINI-PAYLOAD` | `base64(nonce)` | :::note Authentication is required for trading operations and order event subscriptions. Market data streams are available without authentication. ::: :::info Browser WebSocket Clients The standard browser `WebSocket` constructor (`new WebSocket(url)`) does not allow setting custom HTTP headers during the upgrade handshake. For browser-based applications, connect via backend proxy services or use query-string session authentication where supported. Node.js (`ws`), Python, Go, and native clients can supply standard HTTP headers during the handshake. ::: :::warning Authentication headers must be provided during the initial WebSocket handshake—you cannot authenticate after the connection is established. ::: ### Signature Generation Step-by-Step ```pseudo # Create a nonce from the current epoch time in seconds nonce = current_timestamp_in_seconds # Our payload will be the base64 encoded nonce for simplicity payload = base64_encode(nonce.toString) # Generate a signature using the hmac_sha384 algorithm signature = hmac_sha384(payload, api_secret) # Convert the signature to hex so it can be passed in the headers hexSignature = hex(signature) ``` --- ## Alternative: OAuth 2.0 Bearer Token If your application uses [OAuth 2.0](/authentication/oauth) to access the Gemini API, you can authenticate the WebSocket connection with the same access token instead of provisioning an API key. Pass the access token in the `Authorization` header on the WebSocket upgrade request: | Header | Value | |-----------------|--------------------------| | `Authorization` | `Bearer ` | When using OAuth, you do **not** send the `X-GEMINI-APIKEY`, `X-GEMINI-NONCE`, `X-GEMINI-PAYLOAD`, or `X-GEMINI-SIGNATURE` headers. :::note The access token must have scopes that cover the streams you intend to subscribe to (for example, `orders:read` for `orders@account`). See [OAuth scopes](/authentication/oauth#oauth-scopes). ::: :::warning Access tokens are short-lived (default 24 hours). If the token expires during a session, the server will close the connection and you must reconnect with a refreshed token — tokens cannot be rotated on a live connection. See [Using Refresh Tokens](/authentication/oauth#using-refresh-tokens). ::: --- URL: https://developer.gemini.com/rest-api/rest-api.md # REST APIs --- URL: https://developer.gemini.com/rest-api/prediction-markets.md # Prediction Markets import { IconChartLine, IconCheckCircleOutlined, IconDocumentOutlined, IconPlusCircleOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/rest-api/common.md # Common --- URL: https://developer.gemini.com/products/stocks.md # Stocks --- URL: https://developer.gemini.com/products/staking.md # Staking {/* Note: REST API endpoint links and their HTTP method badges are populated in pages/trading/rest-api/staking.mdx */} --- URL: https://developer.gemini.com/products/spot.md # Spot crypto --- URL: https://developer.gemini.com/products/perpetuals.md # Perpetuals --- URL: https://developer.gemini.com/products/margin.md # Margin trading --- URL: https://developer.gemini.com/prediction-markets/trading-quickstart.md # Trading Quickstart This guide walks you through your first Prediction Markets trade using the REST API. You will learn how to verify prerequisites, select contracts, and manage limit orders. For active trading and market making, use the [Prediction Markets WebSocket streams](/prediction-markets/websocket/streams). REST is best for discovery, one-off orders, and state reconciliation. ## Prerequisites Before sending private requests, confirm the following: - You have a Gemini account with Prediction Markets access. - The account is funded. Check available balances with [Get Available Balances](/trading/rest-api/fund-management/get-available-balances). - Your credential supports your chosen interface. For API keys, create an account-scoped key in [API Settings](https://exchange.gemini.com/settings/api). Follow [API key authentication](/authentication/api-key) to generate HMAC headers. For OAuth, request the required [scopes](/authentication/oauth). - The credential has trading permissions. API keys require the `Trader` role. OAuth tokens require the `orders:create` scope for [Place Order](/rest-api/prediction-markets/order-management/place-order). See [Roles and permissions](/roles). - You target the correct account. Account-level keys trade their scoped account. Master keys must specify the account shortname per [Accounts and subaccounts](/rest-api/common/admin/subaccounts). Prediction Markets order endpoints are private REST routes. For API keys, merge endpoint fields with `request` and `nonce`. Base64-encode this payload into `X-GEMINI-PAYLOAD`, sign it, and send an empty HTTP body per [Private API invocation](/authentication/api-key#private-api-invocation). Never expose API secrets in source code or client apps. ## Terms and order vocabulary Prediction Markets terms are versioned and accepted at the account group level. Check status with [Get Terms Status](/rest-api/prediction-markets/terms/get-terms-status). If `hasAcceptedLatest` is `false`, call [Accept Terms](/rest-api/prediction-markets/terms/accept-terms) before placing orders. The REST order fields used in this guide are: | Field | Meaning | | --- | --- | | `symbol` | The contract's exact `instrumentSymbol`, returned by event discovery. | | `orderType` | `limit` for this guide. REST also supports `stop-limit`. | | `side` | `buy` or `sell`. | | `outcome` | `yes` or `no`. | | `quantity` | The number of contracts, represented as a string. | | `price` | The limit price, represented as a string in the `0–1` range. | | `timeInForce` | `good-til-cancel` keeps the order active until it fills or is canceled. It is the default in the schema. | ## Choose an instrument symbol Do not construct a symbol from an event title, contract label, or ticker pattern. Discover an event with [List Events](/rest-api/prediction-markets/events/list-events), or retrieve one with [Get Event](/rest-api/prediction-markets/events/get-event), then select a contract whose status and trading state indicate that it is available to trade. Use the contract's exact `instrumentSymbol` value. For example, an event response can contain a contract like this: ```json { "ticker": "FEDJAN26-DN25", "instrumentSymbol": "GEMI-FEDJAN26-DN25", "status": "active", "marketState": "open" } ``` The value in `instrumentSymbol` is passed as `symbol` to REST order endpoints. Treat the event and contract metadata, including the outcome and expiry, as part of your pre-trade review. ## REST flow The smallest safe REST flow is: 1. Discover an active, open contract and save its exact `instrumentSymbol`. 2. Check and, if necessary, accept the latest Prediction Markets terms. 3. Confirm the account, role or OAuth scope, and available balance. 4. Submit a small limit order with `POST /v1/prediction-markets/order`. 5. Record the returned `orderId`, then query active orders or order history as needed. 6. Cancel the order with `POST /v1/prediction-markets/order/cancel` if it should no longer remain active. 7. Reconcile orders and positions after the response, a timeout, reconnect, or any suspected message gap. ### Place a first limit order The following is the complete API-key payload before base64 encoding. Replace `` and `` with current values, then follow the signing flow in [Private API invocation](/authentication/api-key#private-api-invocation). Do not send this JSON as the HTTP request body: ```json { "request": "/v1/prediction-markets/order", "nonce": "", "symbol": "", "orderType": "limit", "side": "buy", "quantity": "1", "price": "0.50", "outcome": "yes", "timeInForce": "good-til-cancel" } ``` Submit it to: ```text POST https://api.gemini.com/v1/prediction-markets/order ``` See the complete request, response, authentication, roles, and parameter reference in [Place Order](/rest-api/prediction-markets/order-management/place-order). An accepted response includes an `orderId` and an order status such as `open`; acceptance does not mean that the order has filled. ## Monitor the order For a REST-only integration, call [Get Active Orders](/rest-api/prediction-markets/order-management/get-active-orders) to find currently open orders. Use [Get Order History](/rest-api/prediction-markets/order-management/get-order-history) to review filled or canceled orders. Match records using the server `orderId`, and track `filledQuantity` separately from `remainingQuantity` because an order can be partially filled. For live state, authenticate a connection as described in [WebSocket Authentication](/prediction-markets/websocket/authentication) and subscribe to the [`orders@account` order stream](/prediction-markets/websocket/streams#order-events). Order events report transitions such as `NEW`, `OPEN`, `PARTIALLY_FILLED`, `FILLED`, and `CANCELED`. Keep the WebSocket state separate from REST snapshots and rebuild it from REST after startup, reconnects, or detected gaps. ## Cancel the order Cancel by the `orderId` returned when the order was placed: ```json { "orderId": 12345678901 } ``` Submit it to: ```text POST https://api.gemini.com/v1/prediction-markets/order/cancel ``` See [Cancel Order](/rest-api/prediction-markets/order-management/cancel-order) for the exact request and response. A cancellation request does not undo fills that already occurred, so check the final filled quantity and reconcile the resulting position. ## Safety and reconciliation - Use a small quantity for the first order and verify the event definition, outcome, price, and expiry before submitting it. - Treat `instrumentSymbol` as an opaque value returned by Gemini. Do not derive it from display text or assume that symbols are interchangeable across markets. - Persist the event, contract, `instrumentSymbol`, `orderId`, quantities, prices, timestamps, and order status from every response or event. - On a timeout, do not blindly retry a placement. Query [Get Active Orders](/rest-api/prediction-markets/order-management/get-active-orders) and [Get Order History](/rest-api/prediction-markets/order-management/get-order-history) first to determine whether the original request was accepted. - After fills, compare the order's executed quantity with [Get Positions](/rest-api/prediction-markets/positions/get-positions). Use [Get Settled Positions](/rest-api/prediction-markets/positions/get-settled-positions) as the historical source for resolved contracts and payouts. - Use [Position Updates](/prediction-markets/websocket/streams#position-updates) and [Balance Updates](/prediction-markets/websocket/streams#balance-updates) for responsive account state, then use REST snapshots for recovery and audit. - If you enable WebSocket `cancelOnDisconnect`, understand that all open orders placed through that WebSocket session are canceled when it disconnects. See [WebSocket Introduction](/prediction-markets/websocket/introduction). For the full lifecycle from discovery through settlement, see [Order Lifecycle and Settlement](/prediction-markets/order-lifecycle). --- URL: https://developer.gemini.com/prediction-markets/tickers-weather.md # Weather Ticker Format This specification defines the ticker format for weather prediction markets. Use this as the authoritative reference for ticker generation and parsing. ## Overview Weather prediction market tickers follow a standard Gemini format for temperature threshold contracts: ``` GEMI-{Event}-{Contract} ``` ## Ticker Structure | Component | Description | Example | |-----------|-------------|---------| | `GEMI` | Gemini prediction market prefix | `GEMI` | | `Event` | Event type + location + expiry datetime | `WXHIGH-MIA-2603260359` | | `Contract` | Temperature threshold condition | `LO76` | **Full Ticker Example:** - `GEMI-WXHIGH-MIA-2603260359-LO76` ## Event Format The event identifies the weather event type, location, and expiry datetime. ### Structure ``` {TYPE}-{LOCATION}-{YYMMDDHHmm} ``` ### Components | Component | Format | Description | |-----------|--------|-------------| | `TYPE` | `WXHIGH` | Weather event type | | `LOCATION` | `[A-Z]{3}` | Location code for the weather station | | `YYMMDDHHmm` | `[0-9]{10}` | Expiry datetime in UTC (2-digit year, month, day, hour, minute) | ### Expiry Datetime Format ``` YYMMDDHHmm ``` | Position | Part | Description | |----------|------|-------------| | 1-2 | `YY` | Year (e.g., `26` for 2026) | | 3-4 | `MM` | Month (01-12) | | 5-6 | `DD` | Day (01-31) | | 7-8 | `HH` | Hour in UTC (00-23) | | 9-10 | `mm` | Minute in UTC (00-59) | **Examples:** - `2603260359` = March 26, 2026 at 03:59 UTC - `2601010359` = January 1, 2026 at 03:59 UTC - `2507150359` = July 15, 2025 at 03:59 UTC ## Supported Event Types | Event Type | Code | Description | |------------|------|-------------| | Highest Temperature | `WXHIGH` | Highest recorded temperature for the day at the specified location | | Lowest Temperature | `WXLOW` | Lowest recorded temperature for the day at the specified location | Additional weather event types may be added. Check [List Events](/rest-api/prediction-markets/events/list-events) for the current list of available markets. ## Supported Locations Weather contracts use Gemini-specific location codes to identify weather stations. These location codes are different from airport codes or NWS Weather Forecast Office (WFO) codes, although resolution data is sourced from the National Weather Service (NWS). | Location Code | Weather Station | City | State | |---------------|-----------------|------|-------| | `NYC` | Central Park | New York City | NY | | `MDW` | Chicago Midway | Chicago | IL | | `MIA` | Miami International Airport | Miami | FL | | `LAX` | Los Angeles International Airport | Los Angeles | CA | | `BOS` | Boston (Logan Airport) | Boston | MA | Additional locations may be added. Check [List Events](/rest-api/prediction-markets/events/list-events) for the current list of available locations. ## Contract Format Weather contracts support three threshold types based on temperature in degrees Fahrenheit. All boundaries are inclusive. ### Contract Types | Type | Format | Description | Example | |------|--------|-------------|---------| | Less than or equal to | `LO{TEMP}` | Temperature will be ≤ the specified value | `LO76` | | Range | `{TEMP1}TO{TEMP2}` | Temperature will be between the two values (inclusive) | `44TO45` | | Greater than or equal to | `HI{TEMP}` | Temperature will be ≥ the specified value | `HI55` | ### Components | Component | Description | |-----------|-------------| | `LO` | Less than or equal to (≤) indicator | | `HI` | Greater than or equal to (≥) indicator | | `TO` | Range delimiter (inclusive on both ends) | | `TEMP` | Temperature in whole degrees Fahrenheit | ### Temperature Encoding Temperatures are encoded as whole integers representing degrees Fahrenheit. | Temperature | Encoded | |-------------|---------| | 76°F or below | `LO76` | | Between 44°F and 45°F | `44TO45` | | 55°F or above | `HI55` | | 90°F or above | `HI90` | | 32°F or below | `LO32` | ## Complete Examples ### Miami – High Temperature ≤ 76°F **Highest temperature at Miami 76°F or below, expiring March 26, 2025 at 03:59 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Event Type | `WXHIGH` | | Location | `MIA` | | Expiry Datetime | `2503260359` | | Contract | `LO76` | | **Full Ticker** | `GEMI-WXHIGH-MIA-2503260359-LO76` | ### New York City – High Temperature Range **Highest temperature at New York City between 44°F and 45°F, expiring March 26, 2025 at 03:59 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Event Type | `WXHIGH` | | Location | `NYC` | | Expiry Datetime | `2503260359` | | Contract | `44TO45` | | **Full Ticker** | `GEMI-WXHIGH-NYC-2503260359-44TO45` | ### Chicago – High Temperature ≥ 55°F **Highest temperature at Chicago 55°F or above, expiring April 10, 2025 at 03:59 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Event Type | `WXHIGH` | | Location | `MDW` | | Expiry Datetime | `2504100359` | | Contract | `HI55` | | **Full Ticker** | `GEMI-WXHIGH-MDW-2504100359-HI55` | ### Los Angeles – High Temperature ≥ 90°F **Highest temperature at Los Angeles 90°F or above, expiring July 15, 2025 at 03:59 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Event Type | `WXHIGH` | | Location | `LAX` | | Expiry Datetime | `2507150359` | | Contract | `HI90` | | **Full Ticker** | `GEMI-WXHIGH-LAX-2507150359-HI90` | ### Boston – High Temperature ≤ 32°F **Highest temperature at Boston 32°F or below, expiring January 15, 2026 at 03:59 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Event Type | `WXHIGH` | | Location | `BOS` | | Expiry Datetime | `2601150359` | | Contract | `LO32` | | **Full Ticker** | `GEMI-WXHIGH-BOS-2601150359-LO32` | ## Regex Patterns ### Full Ticker ```regex ^GEMI-(WX[A-Z]+)-([A-Z]{3})-(\d{10})-(LO\d+|\d+TO\d+|HI\d+)$ ``` **Capture Groups:** 1. Event type (e.g., `WXHIGH`) 2. Location code (e.g., `MIA`) 3. Expiry datetime (YYMMDDHHmm) 4. Contract (e.g., `LO76`, `44TO45`, `HI55`) ### Event Only ```regex ^(WX[A-Z]+)-([A-Z]{3})-(\d{10})$ ``` **Capture Groups:** 1. Event type 2. Location code 3. Expiry datetime (YYMMDDHHmm) ### Contract Only ```regex ^(LO\d+|\d+TO\d+|HI\d+)$ ``` ## Validation Rules 1. Ticker must start with `GEMI-` 2. Event type must be a supported weather event type 3. Location must be a supported location code 4. Expiry datetime must be a valid UTC datetime in `YYMMDDHHmm` format 5. Contract must be one of: `LO{TEMP}`, `{TEMP1}TO{TEMP2}`, or `HI{TEMP}` 6. Temperatures must be whole integers ## Changelog | Version | Effective Date | Changes | |---------|----------------|---------| | 1.0 | 2026-03-27 | Initial specification | --- URL: https://developer.gemini.com/prediction-markets/tickers-sports.md # Sports Ticker Format This specification defines the ticker format for sports prediction markets. Use this as the authoritative reference for ticker generation and parsing. For event discovery and canonical taxonomy mappings, see [Sports Market Taxonomy](/prediction-markets/sports-market-taxonomy). Do not parse ticker strings to reconstruct the `sportsMarket` object; use API responses directly. ## Overview Sports prediction market tickers follow a consistent hierarchical structure: - **Hierarchical**: Event tickers group related contracts together. - **Sortable**: Alphabetical sorting clusters all markets for a game. - **Parseable**: Deterministic structure supports programmatic parsing. - **Human-readable**: Encodes league, date, teams, and market types compactly. Sports fall into two categories: | Category | Sports | Structure | |----------|--------|-----------| | **Team Sports** | NBA, NFL, NHL, MLB, EPL, World Cup, International Friendlies, NCAAM | Home vs Away matchup format | | **Individual Sports** | Golf, Formula 1 | Tournament/race with N competitors | ## Ticker Hierarchy | Level | Description | Team Sports Example | Individual Sports Example | |-------|-------------|---------------------|---------------------------| | **Event Ticker** | Identifies a specific market | `GEMI-NBA-2602121800-HOU-DAL-S` | `GEMI-GOLF-MAS-WIN-20260412` | | **Contract Ticker** | Identifies a position within that market | `DAL6` | `SCHEFFLER` | | **Full Ticker** | Event + Contract combined | `GEMI-NBA-2602121800-HOU-DAL-S-DAL6` | `GEMI-GOLF-MAS-WIN-20260412-SCHEFFLER` | --- ## Team Sports ### Event Ticker Format **Structure** ``` GEMI-{LEAGUE}-{YYMMDD}{HHMM}-{AWAY}-{HOME}-{TYPE} ``` **Components** | Component | Length | Format | Description | |-----------|--------|--------|-------------| | `GEMI` | 4 | literal | Gemini prediction market prefix | | `LEAGUE` | 2-6 | `[A-Z]+` | League identifier | | `YYMMDD` | 6 | `[0-9]{6}` | Game date (UTC) | | `HHMM` | 4 | `[0-9]{4}` | Game time in 24hr (UTC) | | `AWAY` | 2-4 | `[A-Z]+` | Away team abbreviation | | `HOME` | 2-4 | `[A-Z]+` | Home team abbreviation | | `TYPE` | 1-7 | `[A-Z]{1,7}` | Market type code | **Regex Pattern** ```regex ^GEMI-([A-Z]{2,6})-(\d{6})(\d{4})-([A-Z]{2,4})-([A-Z]{2,4})-([A-Z]{1,7})$ ``` **Capture Groups:** 1. League 2. Date (YYMMDD) 3. Time (HHMM) 4. Away team 5. Home team 6. Market type ### Supported Team Sports Leagues | League | Code | Teams | Abbreviation Length | |--------|------|-------|---------------------| | NBA | `NBA` | 30 | 3 | | NFL | `NFL` | 32 | 2-3 | | NCAA Men's Basketball | `NCAAM` | 350+ | 2-4 | | NCAA Women's Basketball | `NCAAW` | 350+ | 2-4 | | NCAA Football | `NCAAF` | 130+ | 2-4 | | NHL | `NHL` | 32 | 2-3 | | MLB | `MLB` | 30 | 2-3 | | English Premier League | `EPL` | 20 | 3-4 | | FIFA World Cup | `FIFAWC` | 48 | 2-4 | | International Friendlies | `INTLFRIENDLY` | 48 | 2-4 | `INTLFRIENDLY` reuses the same national-team pool as `FIFAWC` — it has no separate roster of its own. ### Market Types #### Standard Markets | Type | Code | Description | Contract Format | |------|------|-------------|-----------------| | Moneyline | `M` | Winner of the game | `{TEAM}` | | Spread | `S` | Point spread | `{TEAM}{LINE}` | | Total | `T` | Combined score over/under | `O{LINE}` or `U{LINE}` | | Team Total | `TT` | Single team score over/under | `{TEAM}O{LINE}` or `{TEAM}U{LINE}` | #### Player Props Player prop market types follow the `PP{STAT}` pattern. Player identifiers use uppercase short names (typically last name, e.g., `LUKA`, `MAHOMES`). **Contract Format:** `{PLAYER}O{LINE}` or `{PLAYER}U{LINE}` ##### Basketball | Type | Code | Description | |------|------|-------------| | Points | `PPPTS` | Player points over/under | | Rebounds | `PPREB` | Player rebounds over/under | | Assists | `PPAST` | Player assists over/under | | 3-Pointers Made | `PP3PM` | Player 3-pointers made over/under | | Steals | `PPSTL` | Player steals over/under | | Blocks | `PPBLK` | Player blocks over/under | | Pts + Reb + Ast | `PPPRA` | Player points + rebounds + assists over/under | ##### Football | Type | Code | Description | |------|------|-------------| | Touchdowns | `PPTD` | Player touchdowns over/under | | Passing Yards | `PPYDS` | Player passing yards over/under | | Rushing Yards | `PPRYDS` | Player rushing yards over/under | | Receiving Yards | `PPRECY` | Player receiving yards over/under | | Receptions | `PPREC` | Player receptions over/under | | Completions | `PPCOMP` | Player completions over/under | ##### Baseball | Type | Code | Description | |------|------|-------------| | Strikeouts | `PPSO` | Pitcher strikeouts over/under | | Hits | `PPHITS` | Player hits over/under | | Home Runs | `PPHR` | Player home runs over/under | | RBIs | `PPRBI` | Player RBIs over/under | | Total Bases | `PPTB` | Player total bases over/under | | Runs | `PPRUNS` | Player runs scored over/under | ##### Ice Hockey | Type | Code | Description | |------|------|-------------| | Goals | `PPGOALS` | Player goals over/under | | Assists | `PPAST` | Player assists over/under | | Points | `PPPTS` | Player points (goals + assists) over/under | | Shots on Goal | `PPSOG` | Player shots on goal over/under | | Saves | `PPSAVES` | Goalie saves over/under | ##### Soccer | Type | Code | Description | |------|------|-------------| | Goals | `PPGOALS` | Player goals over/under | | Assists | `PPAST` | Player assists over/under | | Shots on Target | `PPSOT` | Player shots on target over/under | #### Soccer-Specific (EPL, UCL, MLS, FIFAWC, INTLFRIENDLY) | Type | Code | Description | Contract Format | |------|------|-------------|-----------------| | Moneyline | `M` | 3-way result | `{TEAM}` or `D` (draw) | | To Advance (Winner) | `A` | Which team wins the match and advances. Single-leg knockout matches only — two contracts, no draw. | `{TEAM}` | | Correct Score | `CS` | Exact final scoreline in regulation time. World Cup only. | `{HOME}{H}{AWAY}{A}` | | Team Total | `TT` | A single team's score over/under; sport-specific scope is described in the Team Total section below. | `{TEAM}O{LINE}` or `{TEAM}U{LINE}` | Soccer also uses the standard **Spread (`S`)** and **Total (`T`)** markets above, with half-goal lines (e.g. `BRA2` = Brazil -2.5, `O2` = Over 2.5 goals). The moneyline, spread, and total markets resolve on regulation time plus stoppage only — extra time and penalties are excluded. The **To Advance (Winner)** market (`A`) is the exception: it settles on which team ultimately wins a single-leg knockout match, **including extra time and penalties**. See the **To Advance** contract section below. ### Contract Ticker Formats #### Moneyline (`-M`) **Contract Ticker:** `{TEAM}` | Contract | Meaning | |----------|---------| | `DAL` | Dallas | | `HOU` | Houston | | `D` | Draw (soccer only) | **Full Ticker Example:** `GEMI-NBA-2602121800-HOU-DAL-M-DAL` #### Spread (`-S`) **Contract Ticker:** `{TEAM}{LINE}` | Contract | Meaning | |----------|---------| | `DAL6` | Dallas -6.5 | | `HOU6` | Houston +6.5 | All lines (spreads and totals) use whole numbers only. The `.5` is always implied. For example, `DAL6` means Dallas -6.5 and `O222` means Over 222.5. **Full Ticker Example:** `GEMI-NBA-2602121800-HOU-DAL-S-DAL6` #### Total (`-T`) **Contract Ticker:** `O{LINE}` or `U{LINE}` | Contract | Meaning | |----------|---------| | `O222` | Over 222.5 | | `U222` | Under 222.5 | **Full Ticker Example:** `GEMI-NBA-2602121800-HOU-DAL-T-O222` #### Team Total (`-TT`) The **Team Total** market is an over/under on a **single team's** score rather than the combined game total. Its event ticker is the game ticker with the `-TT` type suffix. Settlement scope and available sides are sport-specific: - **FIFA World Cup soccer:** over lines on each team's goals in **regulation time** (90 minutes plus stoppage); extra time and penalties are **excluded**, consistent with the moneyline, spread, total, and correct-score markets. - **NFL:** over lines on each team's points in the **entire game, including any overtime period** — this is **not** regulation-only. NFL Team Totals are offered on regular-season and postseason games; preseason games are excluded. Whole-number lines imply `.5` (for example, `NEO24` means New England Over 24.5 points), and only over contracts are listed. NFL Team Total resolution data is sourced from NFL.com and the NFL's official Game Statistics and Information System (GSIS), then ESPN, then NBC, CBS, FOX, Amazon Prime Video, Peacock, Netflix, or the official broadcast partner. **Contract Ticker:** `{TEAM}O{LINE}` or `{TEAM}U{LINE}` | Contract | Meaning | |----------|---------| | `ENGO1` | England Over 1.5 goals | | `ARGO0` | Argentina Over 0.5 goals | | `HOUO110` | Houston Over 110.5 | | `HOUU110` | Houston Under 110.5 | | `DALO112` | Dallas Over 112.5 | | `DALU112` | Dallas Under 112.5 | **Full Ticker Example:** `GEMI-FIFAWC-2607152100-ARG-ENG-TT-ENGO1` (England Over 1.5 goals). **NFL Team Total examples:** | Contract | Event ticker | Contract ticker | Instrument symbol | |----------|--------------|-----------------|-------------------| | New England Over 24.5 points | `GEMI-NFL-2609100020-NE-SEA-TT` | `NEO24` | `GEMI-NFL-2609100020-NE-SEA-TT-NEO24` | | Seattle Over 20.5 points | `GEMI-NFL-2609100020-NE-SEA-TT` | `SEAO20` | `GEMI-NFL-2609100020-NE-SEA-TT-SEAO20` | #### To Advance (`-A`) The **To Advance** market (also called the **Winner** market) is offered on **single-leg knockout** soccer matches — for example, World Cup knockout-round games. Because a knockout match must produce a winner (extra time and, if needed, penalties when level after 90 minutes), this market has exactly **two contracts and no draw**. Its event ticker is the moneyline ticker with the `-M` suffix replaced by `-A`. **Contract Ticker:** `{TEAM}` | Contract | Meaning | |----------|---------| | `QAT` | Qatar advances | | `CAN` | Canada advances | **Full Ticker Example:** `GEMI-FIFAWC-2606182200-QAT-CAN-A-QAT` A single-leg knockout match is listed as **both** markets: - **Moneyline** (`-M`) — 3-way, settles on the regulation (90-minute + stoppage) result, and **includes** a `D` draw contract. - **To Advance / Winner** (`-A`) — two contracts, **no draw**, settles on which team wins the match after extra time and penalties. Group-stage matches are listed as a moneyline only. **Eligibility** | Match type | To Advance offered? | |------------|---------------------| | Single-leg knockout (Round of 32/16, quarterfinal, semifinal, final, third-place) | Yes | | Group / league stage | No — moneyline only | | Two-legged tie (aggregate) | No | | Best-of-N series | No | #### Correct Score (`-CS`) The **Correct Score** market lists a grid of exact final scorelines for a match. It is currently offered on **FIFA World Cup** matches only. Each contract is one exact scoreline and resolves **Yes** only if the match ends with that precise score in **regulation time** (90 minutes plus stoppage) — extra time and penalties are **excluded**, consistent with the moneyline, spread, and total markets. Its event ticker is the game ticker with the `-CS` type suffix. **Contract Ticker:** `{HOME}{H}{AWAY}{A}` | Component | Description | |-----------|-------------| | `HOME` | Home team abbreviation | | `H` | Home team goals in regulation | | `AWAY` | Away team abbreviation | | `A` | Away team goals in regulation | | Contract | Meaning | |----------|---------| | `ESP1BEL0` | Spain 1, Belgium 0 | | `ESP0BEL0` | 0–0 draw | | `ESP2BEL1` | Spain 2, Belgium 1 | **Full Ticker Example:** `GEMI-FIFAWC-2607101900-BEL-ESP-CS-ESP1BEL0` The scorelines listed for a match mirror the correct-score markets on the underlying data source, so unlikely blowout scores may not be offered. If a match's regulation score is not among the listed contracts, every contract in the market resolves to No. --- ## Individual Sports Individual sports use a tournament or race-based ticker format. Instead of a home/away matchup, the event represents a competition with N individual competitors, each getting their own contract. ### Event Ticker Format **Structure** ``` GEMI-{SPORT}-{EVENT}-{MARKET}-{YYYYMMDD} ``` **Components** | Component | Format | Description | |-----------|--------|-------------| | `GEMI` | literal | Gemini prediction market prefix | | `SPORT` | `[A-Z0-9]+` | Sport identifier (`GOLF`, `F1`) | | `EVENT` | `[A-Z]{2,5}` | Abbreviated event name | | `MARKET` | `[A-Z]+` | Market type (`WIN` for tournament/race winner) | | `YYYYMMDD` | `[0-9]{8}` | Event end date (when winner is determined) | **Regex Pattern** ```regex ^GEMI-([A-Z0-9]+)-([A-Z]{2,5})-([A-Z]+)-(\d{8})$ ``` **Capture Groups:** 1. Sport 2. Event abbreviation 3. Market type 4. End date (YYYYMMDD) ### Supported Individual Sports | Sport | Code | Event Abbreviation | Competitors | |-------|------|--------------------|-------------| | Golf | `GOLF` | 3-char tournament name | ~60-80 per tournament | | Formula 1 | `F1` | 3-char GP name + `GP` | ~20 per race | ### Contract Ticker Format **Contract Ticker:** `{COMPETITOR}` — uppercase alphabetic name of the competitor. | Sport | Format | Collision Handling | Examples | |-------|--------|-------------------|----------| | Golf | Uppercase last name | Prepend first name | `SCHEFFLER`, `JOHNSMITH` | | Formula 1 | Driver symbol | Pre-assigned in DB | `VER`, `HAM`, `NOR` | ### Golf #### Event Ticker ``` GEMI-GOLF-{ABBREV}-WIN-{YYYYMMDD} ``` | Component | Description | Example | |-----------|-------------|---------| | `GEMI` | Gemini prediction market prefix | `GEMI` | | `GOLF` | Sport identifier | `GOLF` | | `ABBREV` | First 3 uppercase alpha chars of tournament name (after stripping "The " prefix) | `MAS` (Masters), `PGA` (PGA Championship) | | `WIN` | Tournament winner market | `WIN` | | `YYYYMMDD` | Final round date | `20260412` | #### Tournament Abbreviation Rules 1. Strip leading "The " / "the " (case-insensitive) 2. Strip trailing year suffix (e.g., " 2025") 3. Remove all non-alphanumeric characters 4. Uppercase 5. Take first 3 characters | Tournament | Abbreviation | |-----------|--------------| | Masters Tournament | `MAS` | | THE PLAYERS Championship | `PLA` | | PGA Championship | `PGA` | | U.S. Open | `USO` | | The Open Championship | `OPE` | | Arnold Palmer Invitational | `ARN` | | Valero Texas Open | `VAL` | #### Contract Ticker (Player) - **Default:** Uppercase last name, alpha characters only (`SCHEFFLER`, `MCILROY`, `WOODS`) - **On collision:** Prepend first name (`JOHNSMITH`, `JAMESSMITH`) - **Special characters stripped:** `J.J. Spaun` → `SPAUN`, `Si Woo Kim` → `KIM` #### Golf Examples **Masters Tournament, Apr 12 2026** | Market | Event Ticker | Contract | Full Ticker | |--------|-------------|----------|-------------| | Scheffler wins | `GEMI-GOLF-MAS-WIN-20260412` | `SCHEFFLER` | `GOLF-MAS-WIN-20260412-SCHEFFLER` | | McIlroy wins | `GEMI-GOLF-MAS-WIN-20260412` | `MCILROY` | `GOLF-MAS-WIN-20260412-MCILROY` | | Rahm wins | `GEMI-GOLF-MAS-WIN-20260412` | `RAHM` | `GOLF-MAS-WIN-20260412-RAHM` | **PGA Championship, May 17 2026** | Market | Event Ticker | Contract | Full Ticker | |--------|-------------|----------|-------------| | Woods wins | `GEMI-GOLF-PGA-WIN-20260517` | `WOODS` | `GOLF-PGA-WIN-20260517-WOODS` | | Schauffele wins | `GEMI-GOLF-PGA-WIN-20260517` | `SCHAUFFELE` | `GOLF-PGA-WIN-20260517-SCHAUFFELE` | ### Formula 1 #### Event Ticker ``` GEMI-F1-{ABBREV}GP-WIN-{YYYYMMDD} ``` | Component | Description | Example | |-----------|-------------|---------| | `GEMI` | Gemini prediction market prefix | `GEMI` | | `F1` | Sport identifier | `F1` | | `ABBREV` | First 3 uppercase chars of GP location + `GP` | `MIAGP` (Miami), `AUSGP` (Australian) | | `WIN` | Race winner market | `WIN` | | `YYYYMMDD` | Race date | `20260504` | #### Contract Ticker (Driver) Pre-assigned driver symbols from the database (e.g., `VER`, `HAM`, `NOR`, `LEC`). #### Formula 1 Examples **Miami Grand Prix, May 4 2026** | Market | Event Ticker | Contract | Full Ticker | |--------|-------------|----------|-------------| | Verstappen wins | `GEMI-F1-MIAGP-WIN-20260504` | `VER` | `F1-MIAGP-WIN-20260504-VER` | | Hamilton wins | `GEMI-F1-MIAGP-WIN-20260504` | `HAM` | `F1-MIAGP-WIN-20260504-HAM` | | Norris wins | `GEMI-F1-MIAGP-WIN-20260504` | `NOR` | `F1-MIAGP-WIN-20260504-NOR` | --- ## Futures Format Futures use a modified structure with `F` appended to the league code. ### Structure ``` GEMI-{LEAGUE}F-{SEASON}{TYPE}-{SUBJECT} ``` ### Components | Component | Format | Description | |-----------|--------|-------------| | `LEAGUE` | `[A-Z]+` | League code | | `F` | literal | Futures indicator | | `SEASON` | `[0-9]{4}` | Season span (e.g., `2526` for 2025-26) | | `TYPE` | varies | Future type code | | `SUBJECT` | varies | Team or entity | ### Future Types | Type | Code | Example | |------|------|---------| | Championship | `CHAMP` | `GEMI-NBAF-2526CHAMP-LAL` | | Conference | `CONF` | `GEMI-NBAF-2526CONF-WEST-LAL` | | Division | `DIV` | `GEMI-NFLF-2526DIV-AFCN-CLE` | | MVP | `MVP` | `GEMI-NBAF-2526MVP-LUKA` | ### Regex Pattern ```regex ^GEMI-([A-Z]{2,6})F-(\d{4})([A-Z]+)(?:-([A-Z]+))?-([A-Z]+)$ ``` **Capture Groups:** 1. League 2. Season (YYYY format, e.g., `2526`) 3. Future type (CHAMP, CONF, DIV, MVP) 4. Sub-category (optional, e.g., WEST, AFCN) 5. Subject (team or player) --- ## Complete Examples ### NBA Game **Houston @ Dallas, Feb 12 2026 18:00 UTC** | Market | Event Ticker | Contract | Full Ticker | |--------|--------------|----------|-------------| | Dallas win | `GEMI-NBA-2602121800-HOU-DAL-M` | `DAL` | `GEMI-NBA-2602121800-HOU-DAL-M-DAL` | | Houston win | `GEMI-NBA-2602121800-HOU-DAL-M` | `HOU` | `GEMI-NBA-2602121800-HOU-DAL-M-HOU` | | Dallas -6.5 | `GEMI-NBA-2602121800-HOU-DAL-S` | `DAL6` | `GEMI-NBA-2602121800-HOU-DAL-S-DAL6` | | Houston +6.5 | `GEMI-NBA-2602121800-HOU-DAL-S` | `HOU6` | `GEMI-NBA-2602121800-HOU-DAL-S-HOU6` | | Over 222.5 | `GEMI-NBA-2602121800-HOU-DAL-T` | `O222` | `GEMI-NBA-2602121800-HOU-DAL-T-O222` | | Under 222.5 | `GEMI-NBA-2602121800-HOU-DAL-T` | `U222` | `GEMI-NBA-2602121800-HOU-DAL-T-U222` | | Houston Over 110.5 | `GEMI-NBA-2602121800-HOU-DAL-TT` | `HOUO110` | `GEMI-NBA-2602121800-HOU-DAL-TT-HOUO110` | | Dallas Under 112.5 | `GEMI-NBA-2602121800-HOU-DAL-TT` | `DALU112` | `GEMI-NBA-2602121800-HOU-DAL-TT-DALU112` | | Luka Over 30.5 pts | `GEMI-NBA-2602121800-HOU-DAL-PPPTS` | `LUKAO30` | `GEMI-NBA-2602121800-HOU-DAL-PPPTS-LUKAO30` | | Luka Under 10.5 reb | `GEMI-NBA-2602121800-HOU-DAL-PPREB` | `LUKAU10` | `GEMI-NBA-2602121800-HOU-DAL-PPREB-LUKAU10` | ### NFL Game **Buffalo @ Kansas City, Jan 12 2026 18:30 UTC** | Market | Full Ticker | |--------|-------------| | Kansas City win | `GEMI-NFL-2601121830-BUF-KC-M-KC` | | Kansas City -3.5 | `GEMI-NFL-2601121830-BUF-KC-S-KC3` | | Over 47.5 | `GEMI-NFL-2601121830-BUF-KC-T-O47` | | Mahomes Over 2.5 TDs | `GEMI-NFL-2601121830-BUF-KC-PPTD-MAHOMESO2` | | Mahomes Over 299.5 yds | `GEMI-NFL-2601121830-BUF-KC-PPYDS-MAHOMESO299` | ### NCAAM Game **Duke @ UNC, Mar 15 2026 19:00 UTC** | Market | Full Ticker | |--------|-------------| | UNC win | `GEMI-NCAAM-2603151900-DUKE-UNC-M-UNC` | | Duke +3.5 | `GEMI-NCAAM-2603151900-DUKE-UNC-S-DUKE3` | | Over 145.5 | `GEMI-NCAAM-2603151900-DUKE-UNC-T-O145` | ### EPL Match **Arsenal vs Man City, Feb 15 2026 15:00 UTC** | Market | Full Ticker | |--------|-------------| | Arsenal win | `GEMI-EPL-2602151500-ARS-MCI-M-ARS` | | Man City win | `GEMI-EPL-2602151500-ARS-MCI-M-MCI` | | Draw | `GEMI-EPL-2602151500-ARS-MCI-M-D` | | Over 2.5 goals | `GEMI-EPL-2602151500-ARS-MCI-T-O2` | ### World Cup Match **Bosnia & Herzegovina vs Canada, Jun 12 2026 19:00 UTC** | Market | Full Ticker | |--------|-------------| | Canada win | `GEMI-FIFAWC-2606121900-BOS-CAN-M-CAN` | | Bosnia & Herzegovina win | `GEMI-FIFAWC-2606121900-BOS-CAN-M-BOS` | | Draw | `GEMI-FIFAWC-2606121900-BOS-CAN-M-D` | | Canada -1.5 | `GEMI-FIFAWC-2606121900-BOS-CAN-S-CAN1` | | Over 2.5 goals | `GEMI-FIFAWC-2606121900-BOS-CAN-T-O2` | ### World Cup Knockout Match **Qatar vs Canada, knockout round, Jun 18 2026 22:00 UTC** A single-leg knockout match is listed as both a To Advance (Winner) market and a 3-way moneyline: | Market | Event Ticker | Contract | Full Ticker | |--------|--------------|----------|-------------| | Qatar to advance | `GEMI-FIFAWC-2606182200-QAT-CAN-A` | `QAT` | `GEMI-FIFAWC-2606182200-QAT-CAN-A-QAT` | | Canada to advance | `GEMI-FIFAWC-2606182200-QAT-CAN-A` | `CAN` | `GEMI-FIFAWC-2606182200-QAT-CAN-A-CAN` | | Qatar win (regulation) | `GEMI-FIFAWC-2606182200-QAT-CAN-M` | `QAT` | `GEMI-FIFAWC-2606182200-QAT-CAN-M-QAT` | | Canada win (regulation) | `GEMI-FIFAWC-2606182200-QAT-CAN-M` | `CAN` | `GEMI-FIFAWC-2606182200-QAT-CAN-M-CAN` | | Draw (regulation) | `GEMI-FIFAWC-2606182200-QAT-CAN-M` | `D` | `GEMI-FIFAWC-2606182200-QAT-CAN-M-D` | ### World Cup Correct Score **Belgium vs Spain, Jul 10 2026 19:00 UTC** — a grid of exact regulation-time scorelines (World Cup only): | Market | Event Ticker | Contract | Full Ticker | |--------|--------------|----------|-------------| | Spain 1, Belgium 0 | `GEMI-FIFAWC-2607101900-BEL-ESP-CS` | `ESP1BEL0` | `GEMI-FIFAWC-2607101900-BEL-ESP-CS-ESP1BEL0` | | 0–0 draw | `GEMI-FIFAWC-2607101900-BEL-ESP-CS` | `ESP0BEL0` | `GEMI-FIFAWC-2607101900-BEL-ESP-CS-ESP0BEL0` | | Spain 2, Belgium 1 | `GEMI-FIFAWC-2607101900-BEL-ESP-CS` | `ESP2BEL1` | `GEMI-FIFAWC-2607101900-BEL-ESP-CS-ESP2BEL1` | ### International Friendly **Egypt vs Brazil, Jun 6 2026 22:00 UTC** | Market | Full Ticker | |--------|-------------| | Brazil win | `GEMI-INTLFRIENDLY-2606062200-EGY-BRA-M-BRA` | | Egypt win | `GEMI-INTLFRIENDLY-2606062200-EGY-BRA-M-EGY` | | Draw | `GEMI-INTLFRIENDLY-2606062200-EGY-BRA-M-D` | | Brazil -2.5 | `GEMI-INTLFRIENDLY-2606062200-EGY-BRA-S-BRA2` | | Over 2.5 goals | `GEMI-INTLFRIENDLY-2606062200-EGY-BRA-T-O2` | ### Golf Tournament **Masters Tournament, Apr 12 2026** | Market | Event Ticker | Contract | Full Ticker | |--------|-------------|----------|-------------| | Scheffler wins | `GEMI-GOLF-MAS-WIN-20260412` | `SCHEFFLER` | `GOLF-MAS-WIN-20260412-SCHEFFLER` | | McIlroy wins | `GEMI-GOLF-MAS-WIN-20260412` | `MCILROY` | `GOLF-MAS-WIN-20260412-MCILROY` | | Matsuyama wins | `GEMI-GOLF-MAS-WIN-20260412` | `MATSUYAMA` | `GOLF-MAS-WIN-20260412-MATSUYAMA` | ### Formula 1 Race **Miami Grand Prix, May 4 2026** | Market | Event Ticker | Contract | Full Ticker | |--------|-------------|----------|-------------| | Verstappen wins | `GEMI-F1-MIAGP-WIN-20260504` | `VER` | `F1-MIAGP-WIN-20260504-VER` | | Norris wins | `GEMI-F1-MIAGP-WIN-20260504` | `NOR` | `F1-MIAGP-WIN-20260504-NOR` | | Leclerc wins | `GEMI-F1-MIAGP-WIN-20260504` | `LEC` | `F1-MIAGP-WIN-20260504-LEC` | ### Futures | Market | Ticker | |--------|--------| | Los Angeles 2025-26 NBA Championship | `GEMI-NBAF-2526CHAMP-LAL` | | Kansas City AFC West Division | `GEMI-NFLF-2526DIV-AFCW-KC` | | Duke NCAA Tournament | `GEMI-NCAAMF-2526CHAMP-DUKE` | ## Validation Rules ### Team Sports Event Ticker Validation 1. Must start with `GEMI-` 2. Must be ALL UPPERCASE 3. League code must be from supported list 4. DateTime must be valid UTC timestamp 5. Team codes must be valid for the league 6. Market type must be from supported list ### Individual Sports Event Ticker Validation 1. Must start with `GEMI-` 2. Must be ALL UPPERCASE 3. Sport code must be from supported list (`GOLF`, `F1`) 4. Event abbreviation must be 2-5 alpha characters 5. Market type must be valid (`WIN`) 6. Date must be valid `YYYYMMDD` format ### Contract Ticker Validation #### Team Sports | Market Type | Valid Pattern | Examples | |-------------|---------------|----------| | Moneyline | `^[A-Z]{2,4}$` or `^D$` | `DAL`, `HOU`, `D` | | To Advance (Winner) | `^[A-Z]{2,4}$` | `QAT`, `CAN` | | Correct Score | `^[A-Z]{2,4}[0-9]+[A-Z]{2,4}[0-9]+$` | `ESP1BEL0`, `ARG2SUI1` | | Spread | `^[A-Z]{2,4}[0-9]+$` | `DAL6`, `HOU6` | | Total | `^[OU][0-9]+$` | `O222`, `U47` | | Team Total | `^[A-Z]{2,4}[OU][0-9]+$` | `HOUO110`, `DALU112` | #### Individual Sports | Market Type | Valid Pattern | Examples | |-------------|---------------|----------| | Winner (Golf) | `^[A-Z]+$` | `SCHEFFLER`, `MCILROY`, `JOHNSMITH` | | Winner (F1) | `^[A-Z]{2,4}$` | `VER`, `HAM`, `NOR` | ### Full Ticker Validation **Team Sports:** ```regex ^GEMI-[A-Z]{2,6}-\d{10}-[A-Z]{2,4}-[A-Z]{2,4}-[A-Z]{1,7}-[A-Z0-9]+$ ``` **Individual Sports:** ```regex ^GEMI-[A-Z0-9]+-[A-Z]{2,5}-[A-Z]+-\d{8}-[A-Z]+$ ``` ## Changelog | Version | Effective Date | Changes | |---------|----------------|---------| | 1.5 | 2026-07-10 | Added the soccer **Correct Score** market (`-CS`) for FIFA World Cup matches — a grid of exact regulation-time scorelines | | 1.4 | 2026-06-25 | Added the soccer **To Advance (Winner)** market (`-A`) for single-leg knockout matches (e.g. World Cup knockout rounds) | | 1.3 | 2026-06-08 | Added World Cup (`FIFAWC`) and International Friendlies (`INTLFRIENDLY`) to supported soccer leagues, with worked examples | | 1.2 | 2026-03-30 | Added Individual Sports section (Golf, Formula 1) with tournament/race-based ticker format | | 1.1 | 2026-03-04 | Corrected prefix from `GEM-` to `GEMI-` for consistency with crypto tickers | | 1.0 | 2026-02-13 | Initial specification | --- URL: https://developer.gemini.com/prediction-markets/tickers-overview.md # Ticker Overview Gemini prediction markets use structured ticker symbols to uniquely identify events and contracts. All tickers use the `GEMI-` prefix. ## Event and Contract Hierarchy All prediction markets follow a two-level structure: - **Event** - The outcome being predicted (e.g., a game, a price threshold, an election) - **Contracts** - The tradeable positions within that event (e.g., Team A wins, Team B wins) Contract tickers are always mapped to their parent event. When querying the API, you can retrieve all contracts for an event or trade individual contracts directly. ## Automated Market Tickers Markets with programmatic resolution use structured ticker formats that encode event details directly: | Market Type | Format | |-------------|--------| | [Commodities](/prediction-markets/tickers-commodities) | `GEMI-{Commodity}{Expiry}-{Contract}` | | [Crypto](/prediction-markets/tickers-crypto) | `GEMI-{Asset}{Expiry}-{Contract}` | | [Sports](/prediction-markets/tickers-sports) | `GEMI-{League}-{DateTime}-{Away}-{Home}-{Type}-{Contract}` | | [Weather](/prediction-markets/tickers-weather) | `GEMI-{EventType}-{Location}-{Expiry}-{Contract}` | These tickers are deterministic - you can parse them to extract event metadata like expiry time, teams, or underlying asset. ## Other Markets Markets without automated resolution (e.g., political events, custom predictions) use unique identifiers that don't follow a structured format. The event → contract hierarchy still applies - contracts are grouped under their parent event and can be queried via the API. --- URL: https://developer.gemini.com/prediction-markets/tickers-crypto.md # Crypto Ticker Format This specification defines the ticker format for crypto price prediction markets. Use this as the authoritative reference for ticker generation and parsing. ## Overview Crypto prediction market tickers follow a standard Gemini format for price threshold contracts: ``` GEMI-{Event}-{Contract} ``` ## Ticker Structure | Component | Description | Example | |-----------|-------------|---------| | `GEMI` | Gemini prediction market prefix | `GEMI` | | `Event` | Underlying asset + optional duration + expiry datetime | `BTC2603230800` or `BTC05M2602251745` | | `Contract` | Price threshold condition | `HI105000` or `UP` | **Full Ticker Examples:** - `GEMI-BTC2603230800-HI105000` (daily contract) - `GEMI-BTC05M2602251745-UP` (5-minute contract) - `GEMI-BTC15M2602251745-UP` (15-minute contract) Short-duration contracts (5-minute and 15-minute) previously embedded the strike price in the contract segment (e.g., `GEMI-BTC05M2602251745-HI66750`). This format is being phased out in favor of the `UP` contract type, where the strike price is returned in the API response instead of encoded in the ticker. You may still encounter the legacy `HI{PRICE}` format for older contracts. ## Event Format The event identifies the underlying crypto asset and its expiry. ### Structure ``` {UNDERLYING}[{DURATION}]{EXPIRY} ``` ### Components | Component | Format | Description | |-----------|--------|-------------| | `UNDERLYING` | `[A-Z]+` | Crypto asset symbol (e.g., `BTC`, `ETH`, `SOL`, `XRP`) | | `DURATION` | `05M` or `15M` | **Optional.** Duration marker for 5-minute or 15-minute interval contracts. Omitted for all other durations. | | `EXPIRY` | `YYMMDDHHmm` | Expiry date and time in UTC | ### Expiry Format ``` YYMMDDHHmm ``` | Position | Part | Description | |----------|------|-------------| | 1-2 | `YY` | Year (e.g., `26` for 2026) | | 3-4 | `MM` | Month (01-12) | | 5-6 | `DD` | Day (01-31) | | 7-8 | `HH` | Hour in 24hr UTC (00-23) | | 9-10 | `mm` | Minute (00-59) | **Examples:** - `BTC2603230800` = BTC expiring March 23, 2026 08:00 UTC - `BTC05M2602251745` = BTC 5-minute contract expiring February 25, 2026 17:45 UTC - `BTC15M2602251745` = BTC 15-minute contract expiring February 25, 2026 17:45 UTC ### Duration Markers For **5-minute and 15-minute interval contracts only**, a duration marker is inserted after the underlying asset symbol: | Duration | Marker | Format | Example Event | |----------|--------|--------|---------------| | 5 minutes | `05M` | `{UNDERLYING}05M{EXPIRY}` | `BTC05M2602251745` | | 15 minutes | `15M` | `{UNDERLYING}15M{EXPIRY}` | `BTC15M2602251745` | | All others | *(none)* | `{UNDERLYING}{EXPIRY}` | `BTC2603230800` | - Only `05M` and `15M` markers are supported (zero-padded to 3 characters) - All other durations (1 day, 1 week, etc.) omit the duration marker - The marker `5M` without leading zero is **not** recognized—it must be `05M` ## Supported Underlyings | Asset | Symbol | Description | |-------|--------|-------------| | Bitcoin | `BTC` | Bitcoin price in USD | | Ethereum | `ETH` | Ethereum price in USD | | Solana | `SOL` | Solana price in USD | | XRP | `XRP` | XRP price in USD | Additional assets may be added. Check [List Events](/rest-api/prediction-markets/events/list-events) for the current list of available markets. ## Contract Format Gemini crypto prediction contracts use one of two contract types depending on the duration: | Contract Type | Format | Used For | Strike Price Source | |---------------|--------|----------|---------------------| | `UP` | `UP` | Short-duration contracts (5-minute, 15-minute) | API response | | `HI{PRICE}` | `HI` + encoded price | Daily and longer contracts | Embedded in ticker | ### `UP` Contract (Short-Duration) For 5-minute and 15-minute contracts, the contract segment is `UP`. An `UP` contract resolves YES if the underlying asset's price is at or above the strike price at expiry. The strike price is the asset's price at the start of the contract window and is not encoded in the ticker. ``` UP ``` The strike price for `UP` contracts is available through the event response and the [Get Strike Price for Event](/rest-api/prediction-markets/events/get-event-strike) endpoint once it has been captured. ### `HI{PRICE}` Contract (Daily and Longer) For daily and longer-duration contracts, the strike price is encoded directly in the ticker. ``` HI{PRICE} ``` | Component | Description | |-----------|-------------| | `HI` | Greater than or equal to (>=) indicator | | `PRICE` | Strike price as integer, with `D` as decimal delimiter | ### Price Encoding (`HI` contracts only) - Whole numbers are entered as-is: `105000` = $105,000 - Decimals use `D` as delimiter: `2D20` = $2.20 | Price | Encoded | |-------|---------| | $105,000 | `HI105000` | | $2.20 | `HI2D20` | | $0.50 | `HI0D50` | | $3,500.25 | `HI3500D25` | ## Complete Examples ### BTC Contract (Daily) **BTC $105,000 or above, expiring March 23, 2026 08:00 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Underlying | `BTC` | | Expiry | `2603230800` | | Contract | `HI105000` | | **Full Ticker** | `GEMI-BTC2603230800-HI105000` | ### BTC Contract (5-Minute) **BTC UP, expiring February 25, 2026 17:45 UTC (5-minute interval)** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Underlying | `BTC` | | Duration | `05M` | | Expiry | `2602251745` | | Contract | `UP` | | **Full Ticker** | `GEMI-BTC05M2602251745-UP` | ### BTC Contract (15-Minute) **BTC UP, expiring February 25, 2026 17:45 UTC (15-minute interval)** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Underlying | `BTC` | | Duration | `15M` | | Expiry | `2602251745` | | Contract | `UP` | | **Full Ticker** | `GEMI-BTC15M2602251745-UP` | ### XRP Contract **XRP $2.20 or above, expiring March 23, 2026 15:00 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Underlying | `XRP` | | Expiry | `2603231500` | | Contract | `HI2D20` | | **Full Ticker** | `GEMI-XRP2603231500-HI2D20` | ### ETH Contract **ETH $4,500 or above, expiring April 1, 2026 12:00 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Underlying | `ETH` | | Expiry | `2604011200` | | Contract | `HI4500` | | **Full Ticker** | `GEMI-ETH2604011200-HI4500` | ### SOL Contract **SOL $250.50 or above, expiring February 28, 2026 16:00 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Underlying | `SOL` | | Expiry | `2602281600` | | Contract | `HI250D50` | | **Full Ticker** | `GEMI-SOL2602281600-HI250D50` | ## Regex Patterns ### Full Ticker ```regex ^GEMI-([A-Z]+)(?:(05M|15M))?(\d{10})-(UP|HI(\d+(?:D\d+)?))$ ``` **Capture Groups:** 1. Underlying asset 2. Duration marker (optional: `05M` or `15M`) 3. Expiry (YYMMDDHHmm) 4. Contract (`UP` or full `HI{PRICE}`) 5. Price (with optional decimal, only present for `HI` contracts) > This pattern validates ticker structure only. To validate that the contract type matches the duration (e.g., `UP` for short-duration, `HI` for daily and longer), apply additional logic after parsing. ### Event Only ```regex ^([A-Z]+)(?:(05M|15M))?(\d{10})$ ``` **Capture Groups:** 1. Underlying asset 2. Duration marker (optional: `05M` or `15M`) 3. Expiry (YYMMDDHHmm) ### Contract Only ```regex ^(UP|HI(\d+(?:D\d+)?))$ ``` ## Validation Rules 1. Ticker must start with `GEMI-` 2. Underlying must be a valid crypto asset symbol 3. Duration marker, if present, must be exactly `05M` or `15M` 4. Expiry must be a valid future UTC datetime 5. For new contracts, `UP` is used for short-duration and `HI{PRICE}` for daily and longer. Legacy short-duration contracts may still use `HI{PRICE}`. 6. Price decimal delimiter must be `D` (not `.`) for `HI` contracts ## Changelog | Version | Effective Date | Changes | |---------|----------------|---------| | 1.2 | 2026-04-08 | Short-duration contracts (5-min, 15-min) use `UP` contract type; strike price moved to API response. Legacy `HI{PRICE}` format deprecated for these durations. | | 1.1 | 2026-02-25 | Added duration markers for 5-minute (`05M`) and 15-minute (`15M`) interval contracts | | 1.0 | 2026-02-13 | Initial specification | --- URL: https://developer.gemini.com/prediction-markets/tickers-commodities.md # Commodities Ticker Format This specification defines the ticker format for commodities price prediction markets. Use this as the authoritative reference for ticker generation and parsing. ## Overview Commodities prediction market tickers follow the same general Gemini format as crypto tickers — a price threshold contract on a commodity asset: ``` GEMI-{Event}-{Contract} ``` ## Ticker Structure | Component | Description | Example | |-----------|-------------|---------| | `GEMI` | Gemini prediction market prefix | `GEMI` | | `Event` | Commodity code + expiry datetime | `XAU2604021840` | | `Contract` | Price threshold condition | `HI4125` | **Full Ticker Examples:** - `GEMI-XAU2604021840-HI4125` (Gold) - `GEMI-XAG2603271920-HI62` (Silver) - `GEMI-NGAS2603271755-HI2D90` (Natural Gas) - `GEMI-WTI2603281530-HI90` (Oil - WTI) - `GEMI-BRENT2603281530-HI99` (Oil - Brent) - `GEMI-COPPER2604021755-HI5D16` (Copper) ## Event Format The event identifies the commodity asset and its expiry. ### Structure ``` {COMMODITY}{EXPIRY} ``` ### Components | Component | Format | Description | |-----------|--------|-------------| | `COMMODITY` | `[A-Z]+` | Commodity code (e.g., `XAU`, `XAG`, `WTI`, `BRENT`, `NGAS`, `COPPER`) | | `EXPIRY` | `YYMMDDHHmm` | Expiry date and time in UTC | ### Expiry Format ``` YYMMDDHHmm ``` | Position | Part | Description | |----------|------|-------------| | 1-2 | `YY` | Year (e.g., `26` for 2026) | | 3-4 | `MM` | Month (01-12) | | 5-6 | `DD` | Day (01-31) | | 7-8 | `HH` | Hour in 24hr UTC (00-23) | | 9-10 | `mm` | Minute (00-59) | **Examples:** - `XAU2604021840` = Gold expiring April 2, 2026 18:40 UTC - `WTI2603281530` = WTI Oil expiring March 28, 2026 15:30 UTC - `NGAS2603271755` = Natural Gas expiring March 27, 2026 17:55 UTC ## Supported Commodities ### Natural Gas | Code | Description | |------|-------------| | `NGAS` | Natural gas price in USD | **Example:** `GEMI-NGAS2603271755-HI2D90` ### Oil | Code | Description | |------|-------------| | `WTI` | West Texas Intermediate crude oil price in USD | | `BRENT` | Brent crude oil price in USD | **Examples:** - `GEMI-WTI2603281530-HI90` - `GEMI-BRENT2603281530-HI99` ### Copper | Code | Description | |------|-------------| | `COPPER` | Copper price in USD | **Example:** `GEMI-COPPER2604021755-HI5D16` ### Silver | Code | Description | |------|-------------| | `XAG` | Silver price in USD | **Example:** `GEMI-XAG2603271920-HI62` ### Gold | Code | Description | |------|-------------| | `XAU` | Gold price in USD | **Example:** `GEMI-XAU2604021840-HI4125` Additional commodities may be added. Check [List Events](/rest-api/prediction-markets/events/list-events) for the current list of available markets. ## Contract Format All Gemini commodities prediction contracts use the "greater than or equal to" (`HI`) price threshold format. This is the only contract type supported. ### Structure ``` HI{PRICE} ``` ### Components | Component | Description | |-----------|-------------| | `HI` | Greater than or equal to (>=) indicator | | `PRICE` | Strike price as integer, with `D` as decimal delimiter | ### Price Encoding - Whole numbers are entered as-is: `4125` = $4,125 - Decimals use `D` as delimiter: `2D90` = $2.90 | Price | Encoded | |-------|---------| | $4,125 | `HI4125` | | $100 | `HI100` | | $2.90 | `HI2D90` | | $5.16 | `HI5D16` | | $62 | `HI62` | ## Complete Examples ### Gold Contract **Gold $4,125 or above, expiring April 2, 2026 18:40 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Commodity | `XAU` | | Expiry | `2604021840` | | Contract | `HI4125` | | **Full Ticker** | `GEMI-XAU2604021840-HI4125` | ### Silver Contract **Silver $62 or above, expiring March 27, 2026 19:20 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Commodity | `XAG` | | Expiry | `2603271920` | | Contract | `HI62` | | **Full Ticker** | `GEMI-XAG2603271920-HI62` | ### Natural Gas Contract **Natural Gas $2.90 or above, expiring March 27, 2026 17:55 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Commodity | `NGAS` | | Expiry | `2603271755` | | Contract | `HI2D90` | | **Full Ticker** | `GEMI-NGAS2603271755-HI2D90` | ### WTI Oil Contract **WTI Oil $90 or above, expiring March 28, 2026 15:30 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Commodity | `WTI` | | Expiry | `2603281530` | | Contract | `HI90` | | **Full Ticker** | `GEMI-WTI2603281530-HI90` | ### Brent Oil Contract **Brent Oil $99 or above, expiring March 28, 2026 15:30 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Commodity | `BRENT` | | Expiry | `2603281530` | | Contract | `HI99` | | **Full Ticker** | `GEMI-BRENT2603281530-HI99` | ### Copper Contract **Copper $5.16 or above, expiring April 2, 2026 17:55 UTC** | Component | Value | |-----------|-------| | Prefix | `GEMI` | | Commodity | `COPPER` | | Expiry | `2604021755` | | Contract | `HI5D16` | | **Full Ticker** | `GEMI-COPPER2604021755-HI5D16` | ## Regex Patterns ### Full Ticker ```regex ^GEMI-(XAU|XAG|WTI|BRENT|NGAS|COPPER)(\d{10})-HI(\d+(?:D\d+)?)$ ``` **Capture Groups:** 1. Commodity code 2. Expiry (YYMMDDHHmm) 3. Price (with optional decimal) ### Event Only ```regex ^(XAU|XAG|WTI|BRENT|NGAS|COPPER)(\d{10})$ ``` **Capture Groups:** 1. Commodity code 2. Expiry (YYMMDDHHmm) ### Contract Only ```regex ^HI(\d+(?:D\d+)?)$ ``` ## Validation Rules 1. Ticker must start with `GEMI-` 2. Commodity code must be one of: `XAU`, `XAG`, `WTI`, `BRENT`, `NGAS`, `COPPER` 3. Expiry must be a valid future UTC datetime 4. Contract must start with `HI` 5. Price decimal delimiter must be `D` (not `.`) ## Changelog | Version | Effective Date | Changes | |---------|----------------|---------| | 1.0 | 2026-03-26 | Initial specification | --- URL: https://developer.gemini.com/prediction-markets/taker-rewards-program.md # Taker Rewards Program *Effective July 1, 2026, we will pay out up to $1.5M in taker rewards.* **Earn USD rewards by trading on Gemini Predictions** Gemini Predictions is introducing a Taker Rewards Program. The purpose of this program is to grow active trading and deepen markets by rewarding eligible participants who provide taker volume to the order book (i.e., orders that match immediately against resting liquidity). The program runs from July 1 through September 30, 2026, and pays rewards through two mechanisms: a Daily Reward that scales with recent trading activity, and a Monthly Bonus evaluated on monthly volume.
View reward pool remaining on the [Taker Rewards page](https://exchange.gemini.com/predictions/taker-rewards).
## Daily Reward The Daily Reward is paid daily in USD. Each day, your Daily Reward equals your applicable Daily Rate multiplied by the qualifying taker volume you trade that day. Your Daily Rate is determined by your trailing 30-day taker volume (updated daily), as shown in the table below. Daily Rewards begin once a participant has completed 15 Active Trading Days during the Program Term. | Trailing 30-day taker volume | Daily rate | | :---- | :---- | | $500,000 – $1,999,999.99 | 0.50% | | $2,000,000 – $9,999,999.99 | 1.00% | | $10,000,000+ | 1.50% | ## Monthly Bonus The Monthly Bonus is paid in USD within two weeks following the end of each calendar month. Monthly Bonuses are evaluated on calendar-month taker volume. *Full Monthly Periods (July, August, and September 2026)* | Monthly taker volume | Bonus per month | | :---- | :---- | | $500,000 – $999,999.99 | $2,000 | | $1,000,000 – $1,999,999.99 | $5,000 | | $2,000,000 – $4,999,999.99 | $15,000 | | $5,000,000 – $9,999,999.99 | $30,000 | | $10,000,000+ | $50,000 | ## Reward Examples The table below shows combined Daily Reward and Monthly Bonus payouts for a participant sustaining a constant trading volume for a full month. | Monthly taker volume | Daily reward rate | Monthly reward amount | Bonus per month | Total payout | | :---- | :---- | :---- | :---- | :---- | | $500,000 | 0.50% | $2,500 | $2,000 | $4,500 | | $1,000,000 | 0.50% | $5,000 | $5,000 | $10,000 | | $2,000,000 | 1.00% | $20,000 | $15,000 | $35,000 | | $5,000,000 | 1.00% | $50,000 | $30,000 | $80,000 | | $10,000,000 | 1.50% | $150,000 | $50,000 | $200,000 | ## Eligibility To qualify for rewards, taker volume must be executed within the eligible price band. ``` Eligible price band = $0.10 to $0.90 ``` Additional eligibility requirements: - Participants cannot be eligible for both a contracted Market Maker program and the Taker Rewards Program at the same time. - Participants may be eligible for both Maker Rebates and Taker Rewards. - Affiliates of Market Makers are not eligible. - Only bona fide executed trades count toward qualifying volume. Gemini may exclude any trading activity it determines to be abusive, manipulative, non-competitive, coordinated, collusive, or otherwise inconsistent with the purpose of the Program. ## Program Caps Aggregate rewards under the Program, including both Daily Rewards and Monthly Bonuses, are subject to a $1,500,000 Program Cap. Gemini may terminate the Program once the Program Cap is reached. No individual participant may receive more than $250,000 in aggregate rewards under the Program. ## Payment Daily Rewards are paid daily in USD following verification of qualifying activity. Monthly Bonuses are paid in USD within two weeks following the end of each calendar month. Rewards are credited as USD balances to eligible participants' Gemini accounts. --- URL: https://developer.gemini.com/prediction-markets/sports-market-taxonomy.md # Sports Market Taxonomy Sports prediction-market events separate high-level event discovery (`sportsMarket`) from individual tradable contract details (`strike`): - **Event level (`sportsMarket`)**: Classifies the overall sport, market family, subject, settlement scope, and measured statistic. Use this metadata to discover and group events without parsing titles or provider tickers. - **Contract level (`strike`)**: Exposes derived numeric lines (`strike.value`) and direction (`strike.type`: `spread`, `over`, `under`, etc.) on individual contracts. These mappings describe how sports markets are classified when offered. They do not guarantee that every market is currently listed. Use [List Events](/prediction-markets/rest-api/prediction-markets/events/list-events) to discover available events. ## Event-level classification (`sportsMarket`) The `sportsMarket` object is returned on event objects to describe the market's category and rules. ### Market-family distinctions The `subject` distinguishes familiar markets without requiring a separate type for every combination: ### Filtering by event classification The response classification is nested under `sportsMarket`. Event-list filters are flat query parameters: ```bash curl "https://api.gemini.com/v1/prediction-markets/events?status=active&sport=american_football&sports_market_type=prop&sports_market_subject=player&sports_market_scope=full_contest&sports_market_metric=passing_yards" ``` Filters compose independently. A metric-only search such as `sports_market_metric=points` can return markets from any sport using that metric; add `sport` when its sport-specific meaning matters. League and competition filters remain separate from `sportsMarket`. The scope filter selects only `scope.type`. Inspect `scope.ordinal`, `scope.start`, and `scope.end` in the response when you need an exact unit or range. For example: - First half: `{ "type": "half", "ordinal": 1 }` - First quarter: `{ "type": "quarter", "ordinal": 1 }` - First five innings: `{ "type": "inning", "start": 1, "end": 5 }` #### Example query patterns Fetch all active full-contest moneylines across all sports: ```bash curl "https://api.gemini.com/v1/prediction-markets/events?status=active&sports_market_type=moneyline&sports_market_scope=full_contest" ``` Fetch all active spreads (run lines) for baseball: ```bash curl "https://api.gemini.com/v1/prediction-markets/events?status=active&sport=baseball&sports_market_type=spread" ``` Fetch all active game and team totals for baseball: ```bash curl "https://api.gemini.com/v1/prediction-markets/events?status=active&sport=baseball&sports_market_type=total" ``` Fetch all active player props for American football: ```bash curl "https://api.gemini.com/v1/prediction-markets/events?status=active&sport=american_football&sports_market_type=prop&sports_market_subject=player" ``` ## Contract-level strikes (`strike`) Sports contracts include a derived `strike` object populated from contract settlement conditions. Contracts descending from the `Sports` root category expose structured strike information across spreads, totals, player props, and position/rank markets. ### Sports strike derivation matrix The table below summarizes how sports contract strikes are derived from persisted conditions across categories and operators: ` or `<`", "`spread`", "`cond.ValueNumeric`", "Home Team +4.5 / Away Team -4.5"], ["Totals / Props (Over)", "`total_score`, `player_points`, etc.", "`>`", "`over`", "`cond.ValueNumeric`", "Over 220.5 Points"], ["Totals / Props (Over or Equal)", "`player_points`, `player_rebounds`, etc.", "`>=`", "`over_or_equal`", "`cond.ValueNumeric`", "Points >= 25"], ["Totals / Props (Under)", "`total_goals`, `player_assists`, etc.", "`<`", "`under`", "`cond.ValueNumeric`", "Under 2.5 Goals / Assists < 5.5"], ["Position / Rank (Under or Equal)", "`position`, `rank`", "`<=`", "`under_or_equal`", "`cond.ValueNumeric`", "Golf Top 10 Finish (`<= 10`)"], ]} /> Gating and match filter condition keys (such as `match_id`, `match_status`, and `regulation_complete`) are excluded from strike derivation. ## Combined response structure ```json { "eventTicker": "MLB-20260730-TEAM1-TEAM2-SPREAD-5I", "sportsMarket": { "sport": "baseball", "type": "spread", "subject": "team", "scope": { "type": "inning", "start": 1, "end": 5 }, "metric": "runs" }, "contracts": [ { "contractTicker": "MLB-20260730-TEAM1-TEAM2-SPREAD-5I-T1-M0.5", "title": "Home Team -0.5", "strike": { "type": "spread", "value": "-0.5" } } ] } ``` ## Common market mappings A dash in the Metric column means that the market does not require a metric to be discoverable. ### Baseball ### Soccer ### American football The `american_football` sport value covers professional and college football. Use the surrounding league or competition metadata to distinguish NFL from college football. ### Cricket ### Hockey ### Basketball ### Motorsports ### Tennis ### Mixed martial arts ### Golf ## Canonical values The mapping tables cover common market combinations. The OpenAPI schemas remain the canonical source for the complete closed enum values, including sports and metrics not shown in these examples. See the [Prediction Markets schemas](/api-specifications#prediction-markets). Sports ticker encodings are a separate provider-facing representation. Do not derive `sportsMarket` by parsing a ticker; consume the classification returned by the event API. See [Sports Ticker Format](/prediction-markets/tickers-sports) when you specifically need ticker construction or parsing rules. --- URL: https://developer.gemini.com/prediction-markets/order-lifecycle.md # Order Lifecycle and Settlement An order lifecycle does not end when an order is accepted. A reliable prediction-market integration follows the order from submission through fills, position updates, contract resolution, and settlement. ## The lifecycle ```text Discover a contract ↓ Place an order ↓ Observe order events and fills ↓ Reconcile open orders and positions ↓ Wait for contract resolution ↓ Read settled positions and payout ``` ## 1. Discover the contract Start with an active event and select the contract you want to trade. Use the exact `instrumentSymbol` returned by the API for WebSocket subscriptions and order requests. - [List events](/rest-api/prediction-markets/events/list-events) - [Get an event](/rest-api/prediction-markets/events/get-event) - [Prediction Markets ticker overview](/prediction-markets/tickers-overview) Do not construct a tradable symbol from a display name or assume that a ticker format applies to every market type. ## 2. Place and manage the order Before placing an order, confirm that the account has the required role or OAuth scope and has accepted the current Prediction Markets terms. - [Check terms status](/rest-api/prediction-markets/terms/get-terms-status) - [Place an order](/rest-api/prediction-markets/order-management/place-order) - [Cancel an order](/rest-api/prediction-markets/order-management/cancel-order) - [Place batch orders](/rest-api/prediction-markets/order-management/place-batch-orders) - [Cancel batch orders](/rest-api/prediction-markets/order-management/cancel-batch-orders) REST order placement is useful for one-off or server workflows. For active trading and market making, the documented WebSocket `order.place` flow is preferred. REST and WebSocket payloads are not interchangeable; follow the interface-specific reference for field names, casing, and permissions. ## 3. Monitor accepted orders and fills Use authenticated WebSocket order events for live order and fill state. The public trade stream reports market-wide executions, not the authenticated account's fills. Keep local order state keyed by the server order identifier and treat messages as events that may need deduplication or replay protection. After startup, reconnects, or any detected message gap, use REST to rebuild state: - [Get active orders](/rest-api/prediction-markets/order-management/get-active-orders) - [Get order history](/rest-api/prediction-markets/order-management/get-order-history) - [Prediction Markets WebSocket streams](/prediction-markets/websocket/streams) An order being accepted does not mean it has filled. Reconcile open quantity, executed quantity, and fills before updating available strategy capacity. ## 4. Reconcile positions After fills, query positions to confirm the account-level result. Use WebSocket position and balance updates for responsiveness, then use REST as the recovery and audit snapshot. - [Get open positions](/rest-api/prediction-markets/positions/get-positions) - [Get settled positions](/rest-api/prediction-markets/positions/get-settled-positions) - [Get volume metrics](/rest-api/prediction-markets/positions/get-volume-metrics) Keep order state and position state separate. A canceled order can leave a filled position, and a partially filled order can leave both remaining open quantity and an updated position. ## 5. Handle resolution and settlement Resolution determines which outcome is correct for the event. After the contract settles, use the settled-positions response as the authoritative historical record for the position, payout, and resolution side. Do not infer settlement from an event end time alone. Follow the contract status and the documented settled-position response, and preserve the event, contract, order, fill, and position identifiers needed for reconciliation. ## Reliability checklist - Persist order, fill, position, event, and contract identifiers. - Use the exact `instrumentSymbol` returned by the API. - Treat WebSocket updates as the low-latency path and REST snapshots as the recovery path. - Reconcile after reconnects, timeouts, rejected messages, and suspected gaps. - Track partial fills separately from remaining open quantity. - Confirm settlement through the settled-positions endpoint before closing accounting records. ## Next steps - [Trading Quickstart](/prediction-markets/trading-quickstart) - [Market Data](/prediction-markets/market-data) - [Order Management REST API](/rest-api/prediction-markets/order-management) --- URL: https://developer.gemini.com/prediction-markets/market-maker-program.md # Market Maker Program **Apply to provide stable liquidity across a wide range of contracts.** This program rewards approved market makers for providing liquidity across a wide range of contracts in given categories. To learn more or apply, contact [institutional@gemini.com](mailto:institutional@gemini.com). --- URL: https://developer.gemini.com/prediction-markets/market-data.md # Market Data Prediction Markets market data is organized around events and their contracts. Use the public REST API to discover the markets that are currently available and to resolve each contract to its `instrumentSymbol`. Use REST for discovery and snapshots, then use WebSocket streams for low-latency updates. ## Market-data workflow 1. [List active events](#list-active-events) and choose an event. 2. [Get the event details](#get-event-details) using the event `ticker`. 3. [Identify a contract](#identify-a-contract) and save its `instrumentSymbol`. 4. [Read the event snapshot](#read-the-event-snapshot) for the contract’s current pricing and order-book fields. 5. [Subscribe to WebSocket streams](#subscribe-to-websocket-streams) for continuing updates. ## List active events Use [List Events](/rest-api/prediction-markets/events/list-events) with `status=active` to discover events that are currently active. The response is paginated; use `limit` and `offset` to retrieve additional results. You can also filter by `category` or search the event title with `search`. ```bash curl "https://api.gemini.com/v1/prediction-markets/events?status=active&limit=50" ``` Each event includes an event `ticker`, such as `FEDJAN26`, along with its title, status, expiry, and contracts. Keep the event ticker for the next request. Do not construct a contract symbol from the title or slug: use the `instrumentSymbol` returned for the contract. ## Get event details Call [Get Event](/rest-api/prediction-markets/events/get-event) with the event ticker: ```bash curl "https://api.gemini.com/v1/prediction-markets/events/FEDJAN26" ``` The event response provides the authoritative event definition and its `contracts` array. It can also include contract pricing and `contractOrderbooks`. Treat the event and contract `status` values as metadata, and verify that the contract is still available before using it for a live subscription. For crypto Up/Down events, the [Get Strike Price for Event](/rest-api/prediction-markets/events/get-event-strike) endpoint provides strike information when it becomes available. ## Identify a contract Select a contract from the event’s `contracts` array. The fields most useful for market-data clients are: | Field | Use | | --- | --- | | `ticker` | Contract-level identifier returned by the event API. | | `label` | Human-readable outcome, such as `Yes` or `No`. | | `status` | Contract lifecycle status. | | `marketState` | Whether the contract is currently open or closed for trading. | | `instrumentSymbol` | Symbol required by the REST market-data routes and WebSocket stream names. | | `prices` | Current contract pricing, including `bestBid`, `bestAsk`, and `lastTradePrice` when available. | For example, a contract may return: ```json { "ticker": "FEDJAN26-DN25", "label": "Fed cuts at least 25 bps", "status": "active", "marketState": "open", "instrumentSymbol": "GEMI-FEDJAN26-DN25" } ``` Use the exact `instrumentSymbol` value, including capitalization and punctuation, in subsequent requests. The instrument identifies the contract's proposition in YES space. Orders select the `yes` or `no` outcome separately; do not append an outcome to the symbol unless Gemini returned it as part of the symbol. ## Read the event snapshot The Prediction Markets event response is the REST discovery snapshot. Its contract records can include current `prices` and `contractOrderbooks` fields, alongside the contract status and `instrumentSymbol`. Use those fields for discovery and initial display, then use the Prediction Markets WebSocket streams for live book and trade updates. Prices and quantities are returned as strings. Preserve that precision instead of converting values to binary floating-point numbers. After a disconnect or a missed update, request the event again and resubscribe before resuming local state. ## Subscribe to WebSocket streams Connect to `wss://ws.gemini.com` and subscribe using the contract’s `instrumentSymbol`. The [Prediction Markets WebSocket introduction](/prediction-markets/websocket/introduction) documents the connection, and the [Stream Reference](/prediction-markets/websocket/streams) documents payloads and stream names. Choose a stream based on the data you need: | Stream | Use | | --- | --- | | `{instrumentSymbol}@bookTicker` | Real-time best bid and ask. | | `{instrumentSymbol}@depth5`, `@depth10`, or `@depth20` | Periodic top-of-book snapshots. | | `{instrumentSymbol}@depth` or `@depth@100ms` | Differential depth updates for maintaining a local order book. | | `{instrumentSymbol}@trade` | Real-time executions. | For a differential depth stream, connect with `snapshot=-1` to receive a full initial order-book snapshot, then apply subsequent updates. Public depth is normalized in YES space; derive NO notional with `1 - yesPrice`. If update IDs skip ahead, discard the local book and resubscribe to resynchronize. The stream reference also explains how zero quantities remove price levels. WebSocket subscriptions use the symbol returned by the event API. For example, a subscription name for the contract above is: ```text GEMI-FEDJAN26-DN25@bookTicker ``` Use WebSocket as the primary source for active monitoring and trading workflows. Keep the REST event and market-data endpoints available for discovery, initialization, reconnect recovery, and audit snapshots. --- URL: https://developer.gemini.com/prediction-markets/maker-rebate-program.md # Maker Rebate Program **Earn daily USD rebates by providing liquidity on Gemini Predictions** Gemini Predictions has implemented a Maker Rebate Program. The purpose of this program is to increase liquidity, which will improve market depth and tighten spreads, to support efficient pricing for participants by incentivizing eligible market participants to provide resting liquidity to the order book via maker orders (i.e., orders that do not match immediately and rest on the order book). ## Maker Rebates Rebates for maker orders are calculated as a percentage of [taker fees](https://www.gemini.com/fees/predictions) (with a maximum of 5% of fill notional value). This ensures makers are rewarded proportionally to the taker fee value their liquidity generates. Rebates are calculated using the following formula for all contracts created on or after March 18, 2026: ``` Maker Rebate = Rebate Rate × Taker Rate × C × P × (1 − P) ``` | Variable | Description | |----------|-------------| | `C` | Number of contracts traded | | `P` | Price of the contracts in dollars (50 cents is .5) | ## Rebate Rates Rebate rates may differ by market and are subject to change. The dated schedule below describes the published program periods. Rates, eligible categories, and terms may change; check the current [Gemini fee schedule](https://www.gemini.com/fees/predictions) and the applicable API response before relying on a rate. | Market | Period | Rebate Rate | |--------|--------|-------------| | Crypto and Commodities | April 9, 2026 - June 10, 2026 | 0.70 | | | June 11, 2026+ | 0.30 | | Politics | May 10, 2026 - June 10, 2026 | 0.70 | | | June 11, 2026+ | 0.30 | | Sports | April 9, 2026 - June 10, 2026 | 0.50 | | | June 11, 2026+ | 0.30 | | Economics | May 10, 2026 - June 10, 2026 | 0.50 | | | June 11, 2026+ | 0.30 | | All other markets | March 18, 2026+ | 0.30 | ## Rebate Examples | Order Type | Price | Contracts | [**Taker Rate**](https://www.gemini.com/fees/predictions#taker-fees) | Rebate Rate | Maker Rebate | [**Maker Fee**](https://www.gemini.com/fees/predictions#maker-fees) | Maker Net | |------------|-------|-----------|------------|-------------|--------------|-----------|-----------| | Maker | $0.20 | 100 | 0.07 | 0.30 | $0.33 | $0.28 | +$0.05 | | Maker | $0.40 | 100 | 0.07 | 0.30 | $0.50 | $0.42 | +$0.08 | | Maker | $0.50 | 100 | 0.07 | 0.30 | $0.52 | $0.44 | +$0.08 | | Maker | $0.80 | 100 | 0.07 | 0.30 | $0.33 | $0.28 | +$0.05 | ## Rebate Precision Rebates are rounded down to the next cent. ## Eligibility To qualify for a rebate, a maker order must match with an execution price that falls within the eligible price band. ``` Eligible price band = $0.20 to $0.80 ``` Rebates only apply to contracts listed on or after March 18, 2026. ## Payment Rebates are paid daily at 5pm ET in USD. --- URL: https://developer.gemini.com/prediction-markets/maker-fee-free-trading-promo.md # Maker Fee-Free Trading Promo **Pay $0.00 maker fees on qualifying maker-side trades in Gemini Predictions beginning September 2, 2026.** Gemini Predictions is waiving maker fees in full on qualifying maker-side trades across all event contract markets. This promotion lowers the cost of posting resting liquidity, tightening spreads and deepening order books. ## How It Works Any maker-side trade you execute in an Eligible Event Contract from September 2 through December 31, 2026 automatically qualifies for a $0.00 maker fee. No enrollment, minimum volume, or scoring is required. A trade counts as maker-side if it results from a resting order that is subsequently matched against an incoming order. Only executed trades qualify. ## Eligibility | Rule | Value | | :---- | :---- | | Program Term | September 2 through December 31, 2026 | | Eligible markets | All event contracts listed for trading during the Program Term | | Eligible participants | All users in good standing, excluding those affiliated with Gemini Titan | | Qualifying trade | Executed maker-side trade | All trading remains subject to the Exchange Rulebook. Participants found engaging in excluded activity are disqualified from the waiver and may face disciplinary action. Gemini Titan may modify or end the Program at any time, subject to applicable CFTC rules and regulations. --- URL: https://developer.gemini.com/prediction-markets/liquidity-rewards-program.md # Liquidity Rewards Program **Earn daily USD rewards by providing high-quality resting liquidity on Gemini Predictions** The Liquidity Rewards Program pays makers daily USD rewards for providing resting liquidity on Gemini Predictions. Unlike maker rebates that require fills, liquidity rewards score resting orders regardless of whether they execute.
View active reward pools on the [Liquidity Rewards page](https://exchange.gemini.com/predictions/liquidity-rewards). Or via API - [Get Config](/rest-api/prediction-markets/rewards/get-liquidity-rewards-config) - [List Events](/rest-api/prediction-markets/rewards/list-liquidity-rewards-events) - [Get Daily Summary](/rest-api/prediction-markets/rewards/get-liquidity-rewards-daily-summary) - [Get Lifetime Summary](/rest-api/prediction-markets/rewards/get-liquidity-rewards-lifetime-summary)
## How It Works Each eligible event features a daily USD reward pool. Gemini distributes this pool proportionally to makers based on order book presence throughout the day. Scoring favors tight spreads around the midpoint, larger order sizes (up to a cap), two-sided quoting, and high uptime. Quotes outside allowable spreads or below minimum sizes receive zero score. ## How Payouts Are Calculated Gemini calculates scores from per-minute order book snapshots and distributes the pool proportionally: ### Snapshot score Computed every minute for each maker on each contract: ``` Snapshot score = Spread weight × Size × Two-sided multiplier ``` | Variable | Description | |----------|-------------| | Spread weight | Quadratic curve based on quote distance from the midpoint. Tighter quotes score disproportionately higher. When the book is one-sided, the best price on your side is used as the reference instead. | | Size | Order quantity, capped per the size cap. | | Two-sided multiplier | Applied at any snapshot where the maker has a qualifying bid and ask on the same contract. | ### Daily payout At the end of each day, snapshot scores are summed and the pool is split: ``` Daily payout = Pool × (Daily score / Total qualifying score) ``` | Variable | Description | |----------|-------------| | Pool | The reward pool's daily USD budget. | | Daily score | Sum of the maker's snapshot scores across all contracts in the event for the day. | | Total qualifying score | Sum of daily scores across all qualifying makers. | ## Reward Pools Reward pools may be assigned at either the event or contract level: - **Event-level pools** apply to a single event (for example, a marquee sports matchup, an election night, or a one-off macro event). - **Contract-level pools** apply to a recurring contract type (for example, BTC 15-minute hourlies or ETH end-of-day) and cover all instances of that contract type during the period. Daily pool sizes typically range from $10 to $1,000 per day. Pool size and duration vary and are subject to change. Active pools are published daily on the Liquidity Rewards page and available via the API. ## Examples Three makers compete on a Bitcoin event with an event-level pool of $500/day. The current bid-ask is $0.49 to $0.52, so the midpoint is $0.505. ### Example 1: A single snapshot At one minute during the day, the orderbook looks like this: | Maker | Bid | Ask | Distance from $0.505 mid | Two-sided? | Snapshot share of scores | |-------|-----|-----|--------------------------|------------|--------------------------| | Maker A | $0.48 x 50 | $0.52 x 50 | 1.5–2.5¢ | Yes (1.5x bonus) | ~74% | | Maker B | $0.40 x 200 | $0.60 x 200 | 9.5–10.5¢ | Yes (1.5x bonus) | ~5% | | Maker C | $0.49 x 30 | (none) | 1.5¢ | No | ~21% | Maker A dominates this snapshot even though Maker B has 4x the total size on the book. A quote 1.5¢ from the midpoint scores roughly 50x a quote 10¢ away, so tight quoting compounds. Maker B's larger size barely makes up for the wider spread. ### Example 2: At end of day After accumulating snapshot scores across the full day, the pool is split: | Maker | Uptime during day | Qualifies? | Share of pool | Daily payout | |-------|-------------------|------------|---------------|--------------| | Maker A | 96% | Yes | 77% | $386.50 | | Maker B | 35% | No, below uptime threshold | 0% | $0.00 | | Maker C | 83% | Yes | 23% | $113.50 | Maker B's tight-snapshot scores are wasted. Stepping off the book too often disqualifies Maker B for the day. Maker A and Maker C split the full $500 pool. ## Qualification Rules The following parameters apply program-wide to all events with active reward pools. Values may be revised over time as the program evolves. Active values are always reflected on this page and via the API. | Rule | Value | |------|-------| | Maximum spread | 10¢ from midpoint | | Minimum size | 10 contracts | | Size cap | 250 contracts | | Two-sided multiplier | 1.5x | | Uptime threshold | 50% of eligible snapshots | ## Reward Precision The minimum daily payout is $1.00. Daily totals below this threshold are not distributed. Rewards are rounded down to the nearest cent. ## Eligibility For a resting order to count toward your score: - The order must be on a contract that is actively tradeable at the snapshot. - The contract must be associated with an event or contract type that has an active reward pool. - The order must satisfy the qualification rules above. Contracts in post-only mode are excluded from scoring. House and test accounts are excluded from rewards. The Maker Rebate Program operates independently. Orders may earn from both programs simultaneously. ## Payment Rewards are paid daily at 5:30pm ET in USD. --- URL: https://developer.gemini.com/prediction-markets/core-concepts.md # Core Concepts Prediction markets let you trade contracts whose value depends on the outcome of a defined event. A contract is a time-bound position on whether a proposition resolves **YES** or **NO**. ## The mental model Think of a prediction market as a question with tradable answers: ```text Event (the question) └── Contract / instrument (the proposition, expressed in YES space) ├── YES position └── NO position ``` - **Event**: The question being resolved, including rules, timing, and resolution source (e.g., "Will BTC settle at or above $100,000?"). - **Contract**: A tradable instrument representing one proposition within an event. Instruments are defined in YES space: the label, ticker, and symbol define what resolving YES means. - **Outcome**: The contract side: `yes` or `no`. The outcome describes position exposure, not order direction (`buy` or `sell`). - **Position**: Your quantity of a contract outcome. You can open, reduce, or close positions by trading the instrument. The event defines the question. The contract defines the YES-space proposition. The outcome defines your YES or NO exposure. To discover events and inspect contracts, use [List Events](/rest-api/prediction-markets/events/list-events) and [Get Event](/rest-api/prediction-markets/events/get-event). For ticker formats, see [Ticker Overview](/prediction-markets/tickers-overview). ## YES and NO positions For a binary contract, YES and NO are complementary outcomes on the same tradable instrument: | Position | Resolves to a payout when the event... | | --- | --- | | **YES** | Resolves in favor of the stated proposition | | **NO** | Does not resolve in favor of the stated proposition | For example, consider an event containing this contract: > Will the Federal Reserve lower its target rate by at least 0.25% at the specified meeting? The API returns this instrument: ```text GEMI-FEDJAN26-DN25 ``` Buying `yes` expresses that the proposition will resolve YES. Buying `no` expresses that it will resolve NO. Both orders use the same YES-space `instrumentSymbol`; the `outcome` field selects your exposure. If the contract resolves YES, the YES outcome pays the defined settlement amount. If it resolves NO, the NO outcome pays instead. Always check event resolution rules and metadata for settlement details. ## `instrumentSymbol` `instrumentSymbol` is the complete, exchange-recognized identifier for a tradable contract. It includes the `GEMI-` prefix, event identifier, and contract suffix: ```text GEMI-FEDJAN26-DN25 ``` Use the returned `instrumentSymbol` when requesting market data or placing orders. Do not infer symbols by concatenating event strings. Discover contracts through event endpoints to retrieve the exact symbol. See [Ticker Overview](/prediction-markets/tickers-overview) for ticker structures. ## Price as market-implied probability Prediction-market prices are quoted in the contract’s settlement currency. For a binary contract with a $1.00 settlement value, a YES price of `$0.65` indicates an implied probability of **65%**: ```text implied probability ≈ contract price / settlement value ≈ $0.65 / $1.00 ≈ 65% ``` This calculation interprets current market price, not a guaranteed probability. Public order books are normalized in YES space. To calculate NO price, use `1 - yesPrice`. Spreads, liquidity, and book depth determine executable prices. For the complementary NO outcome, the implied probability is the remaining percentage. In practice, YES and NO prices may not total exactly `$1.00` due to spreads and market depth. Read event and contract definitions before comparing prices to external probability models. ## Prediction contracts versus underlying assets Prediction-market contracts can reference underlying assets (e.g., BTC, ETH, commodities, sports teams), but they do not grant asset ownership. | Prediction-market contract | Underlying asset ownership | | --- | --- | | Exposure is to a defined event outcome | Exposure is to asset market value or ownership rights | | Settles to a fixed amount per contract rules | Value fluctuates continuously with the asset | | Does not represent physical or tokenized units | Represents the asset itself or a direct claim | | Identified and traded via `instrumentSymbol` | Traded on spot/derivative markets with asset custody | For example, buying the YES outcome on a BTC prediction contract does not purchase BTC. It creates exposure to the contract resolution condition. The contract can expire worthless even if BTC rises if the resolution condition is not met. Before trading, use [List Events](/rest-api/prediction-markets/events/list-events) to find events, [Get Event](/rest-api/prediction-markets/events/get-event) to read definitions, and [Ticker Overview](/prediction-markets/tickers-overview) to verify symbols. --- URL: https://developer.gemini.com/market-data/symbols-and-minimums.md # Symbols and minimums Quantity and price on incoming orders are strictly held to the minimums and increments shown in the live API reference table below. > [!NOTE] > This page specifically covers **Crypto Trading** pairs, minimum order sizes, tick sizes, and price increments. > For **Event Contracts & Prediction Markets**, tickers, taxonomy, and contract details are handled separately — see [Prediction Market Tickers & Taxonomy](/prediction-markets/tickers-overview). ## Precision on the exchange Quantity and price on incoming orders are strictly held to the minimums and increments on the table shown below. However, once on the exchange, quantities and notional values may exhibit additional precision down to two decimal places past the "minimum order increment" listed below. For instance, it is possible that a `btcusd` trade could execute for a quantity of `0.0000000001` (1e-10) BTC. This is due to: - Incoming market orders that may result in partial fills - Fees - Holds This additional precision is marketable once on the exchange. Your account balances are maintained to full fractional precision in each currency. --- ## Available Trading Symbols (Live from API) The table below fetches live minimum order sizes, tick sizes, and price increments directly from the Gemini API. --- URL: https://developer.gemini.com/get-started/sandbox.md # Demo environment (Sandbox) Gemini's [demo environment](https://exchange.sandbox.gemini.com) (sandbox) provides full exchange functionality with test funds. - Automated bots simulate order book activity and trading. - All balances are for testing. Only Bitcoin Testnet deposits and withdrawals are supported. ## Demo environment URLs ## Create a demo account Register for a test account at the [sandbox site](https://exchange.sandbox.gemini.com). - Use the website to familiarize yourself with Gemini workflows. - Use the API to test trading strategies before connecting to production. Gemini automatically credits new accounts with test balances ($100,000 USD, 1,000 BTC, 20,000 ETH, 20,000 BCH, 20,000 ZEC, and 20,000 LTC). Except for Testnet BTC, the sandbox does not support deposits or withdrawals. The sandbox does not send email notifications. If your integration tests require email delivery or balance adjustments, contact [trading@gemini.com](mailto:trading@gemini.com). ## Two-Factor Authentication Two-factor authentication (2FA) is enabled by default. To bypass 2FA during automated testing: 1. On the 2FA entry screen, set the cookie or HTTP header `GEMINI-SANDBOX-2FA=true` (e.g., run `document.cookie = "GEMINI-SANDBOX-2FA=true; path=/";` in your browser console). 2. Enter `9999999` as your 2FA verification code. --- URL: https://developer.gemini.com/get-started/gemini-staking.md # Gemini Staking To learn more about Staking, please visit https://www.gemini.com/staking --- URL: https://developer.gemini.com/fix-api/fix-api.md # FIX APIs --- URL: https://developer.gemini.com/changelog/upcoming-changes.md # Upcoming Changes TBD --- URL: https://developer.gemini.com/changelog/revision-history.md # Revision History ## Changes These release notes list changes to Gemini Exchange API. ### [2026-09-09](#2026-09-09) - **Prediction Markets** - **Sports Ticker Format** - Corrected Team Total (`-TT`) documentation to distinguish FIFA World Cup regulation-time goals from NFL full-game points including overtime, including NFL eligibility, source agencies, and over-only contracts; added NFL examples and removed the World-Cup-only wording ### [2026-09-03](#2026-09-03) - **Prediction Markets** - **Combo RFQ WebSocket API** - Added the optional maker `clientId` to `rfq.submit_quote`; Gemini returns it as `c` on the maker's authenticated fill event, subject to printable-ASCII and 36-character limits ### [2026-09-02](#2026-09-02) - **Prediction Markets** - **Sports Market Taxonomy** - Added the Tennis "Exact match score" market (`correct_score` + `contest` + `full_contest`) to the market mapping table, and clarified how `correct_score` markets are distinguished from `moneyline` markets - **Prediction Markets** - **WebSocket Streams** - Corrected settlement stream documentation: `settlements@account` is not a production stream; terminal settlement details are delivered through `positions@account`, while historical settled positions remain available through REST ### [2026-08-27](#2026-08-27) - **Prediction Markets** - **Combo RFQ WebSocket API** - Added an optional per-leg instrument symbol (`s`) to each leg in the `requestForQuote` broadcast's `l` array, present when the leg's request included one ### [2026-08-26](#2026-08-26) - **Prediction Markets** - **Maker Fee-Free Trading Promo** - Added a Maker Fee-Free Trading Promo waiving maker fees in full on qualifying maker-side trades across all event contract markets from September 2 through December 31, 2026 ### [2026-08-24](#2026-08-24) - **Prediction Markets** - **Combo RFQ WebSocket API** - Enabled the public RFQ discovery stream, authenticated RFQ delivery streams, and maker quote methods in production for eligible accounts - **Go SDK** - **WebSocket RFQ Support** - Added typed RFQ discovery and delivery subscriptions plus typed submit, withdraw, and confirm quote methods ### [2026-08-13](#2026-08-13) - **Prediction Markets** - **Documentation** - Removed the incorrect "Short-circuit resolution" section from the Combo Contracts overview page. The section claimed a combo settles NO immediately upon the first leg resolving NO without waiting for remaining legs, which does not match the Terms & Conditions (Rule 13.165(f)(1) "Timeline and Sequencing") ### [2026-08-11](#2026-08-11) - **TypeScript SDK** - **Documentation** - Added TypeScript SDK documentation under SDKs & Tools: quickstart, authentication, WebSocket, error handling, and patterns guides - Added REST API reference covering all 105 operations across 7 namespaces with code examples - Added WebSocket reference documenting every stream, method, and wire-format field - Added deep-dive guides for order book reconstruction, RFQ protocol, data types, request validation, WebSocket sessions, and transport internals ### [2026-08-10](#2026-08-10) - **REST** - **Account Services** - Fixed `Balance._timestamp` field type from `integer` (int64) to `string` (date-time) to match the actual ISO 8601 timestamp returned by both sandbox and production environments ### [2026-08-05](#2026-08-05) - **TypeScript SDK** - **Diagnostics and Errors** - Added SDK-wide safe structured diagnostics, response correlation metadata, stable error codes/categories, operation context, shared redaction, and opt-in raw-body error serialization while preserving existing REST results and domain response data - **TypeScript SDK** - **WebSocket Reliability** - Added explicit heartbeat lifecycle control, bounded WebSocket connection and request operations, reconnect replay status events, stream failure visibility, abortable listeners, liveness checks, and inbound frame-size limits ### [2026-08-04](#2026-08-04) - **TypeScript SDK** - **REST API** - Added documented-shape validation for approved state-changing request bodies before authentication and network dispatch, including typed validation errors with operation, field, and rule metadata - **TypeScript SDK** - **Transport Reliability** - **TypeScript SDK** - **Transport Reliability** - Added bounded REST, OAuth, and WebSocket execution, cancellable pagination with item ceilings, and opt-in safe-read retries with Retry-After support; mutations are never retried automatically. ### [2026-08-03](#2026-08-03) - **Prediction Markets** - **REST API** - Added contract-level quantity and price precision metadata (`quantityIncrement`, `quantityMinimum`, `priceIncrement`, `priceMinimum`, and `quoteAssetPrecision`) so clients can validate order values against each instrument's configured increment and minimum rather than a fixed grid ### [2026-07-30](#2026-07-30) - **WebSocket** - **TypeScript SDK** - Added a WebSocket SDK inventory for contract coverage, public API shape options, authentication constraints, and known AsyncAPI drift blockers - Added generated package-visible WebSocket message types with a deterministic AsyncAPI drift check - Added a shared WebSocket session foundation for request correlation, durable subscriptions, authenticated upgrade headers, and cleanup semantics ### [2026-07-29](#2026-07-29) - **Prediction Markets** - **Sports Strike Derivation** - Documented contract-level sports strike derivation (`spread`, `over`, `over_or_equal`, `under`, `under_or_equal`) across sports spreads, totals, player props, and position/rank contracts in the sports market taxonomy and OpenAPI schema - **Market Data** - **TypeScript SDK** - Added endpoint verification coverage and a reproducible verification ledger for every generated Market Data REST wrapper - **Margin** - **TypeScript SDK** - Added no-network endpoint contract verification coverage and reproducible verification ledger entries for every generated Margin REST wrapper - **Perpetuals** - **TypeScript SDK** - Added no-network endpoint contract verification coverage and reproducible verification ledger entries for every generated Perpetuals REST wrapper - **Perpetuals** - **Clearing & Instant** - **OpenAPI** - Corrected generated contract drift by making the Perpetuals funding-payment file report payload optional and defining the required Instant execute `price` request property - **Trading** - **TypeScript SDK** - Added no-network endpoint contract verification coverage, mutation guardrails, and reproducible verification ledger entries for every generated Trading REST wrapper - **Clearing & Instant** - **TypeScript SDK** - Added no-network endpoint contract verification coverage, workflow guardrails, and reproducible verification ledger entries for every generated Clearing & Instant REST wrapper - **Account Services** - **TypeScript SDK** - Added no-network endpoint contract verification coverage, controlled-mutation guardrails, and reproducible verification ledger entries for every generated Account Services REST wrapper - **Prediction Markets** - **Combo Contracts** - Added REST documentation for canonical combo creation, including signed-auth access, two-to-six-leg validation, canonical deduplication, and `200`/`201` response behavior; updated combo discovery responses and default active-status filtering - **Market Data** - **TypeScript SDK** - Completed packed-package Market Data REST facade coverage with public client exports, authenticated and file method consumer checks, documented file response metadata, and live-response contract fixes ### [2026-07-28](#2026-07-28) - **Prediction Markets** - **REST Positions** - **WebSocket Streams** - Added `POST /v1/prediction-markets/positions/settled` endpoint to retrieve historical settled positions with payout amounts, cost basis, and profit/loss calculations. Includes optional `withCashOuts` parameter for early position exits. Results are limited to the past year. - Added terminal settlement details to `positions@account` position reports. A settled event-contract position is emitted once with a zero `position` amount and a `settlement_payout` amount carrying the payout currency and outcome. ### [2026-07-28](#2026-07-28) - **WebSocket** - **Introduction** - Corrected the real-time trading feature summary to remove unsupported order modification ### [2026-07-27](#2026-07-27) - **Prediction Markets** - **REST Volume** - Added public daily and hourly prediction-market trade-volume documentation for one UTC date at a time, including flat category-path rows and `404 NOT_FOUND` for pre-launch or incomplete dates - **Market Data** - **TypeScript SDK** - Added an on-demand packed-package live verification harness with QA routing support, redacted reports, and deterministic offline coverage ### [2026-07-23](#2026-07-23) - **Prediction Markets** - **Sports Market Discovery** - Added a sports market taxonomy reference with field definitions, filters, scope qualifiers, and common market mappings across ten sports - **Prediction Markets** - **TypeScript SDK** - Corrected order-book gap detection to follow Gemini's overlapping WebSocket sequence convention ### [2026-07-22](#2026-07-22) - **Prediction Markets** - **API Specifications** - **SDKs & Tools** - Aligned OpenAPI contract examples with the single-instrument YES/NO outcome model and clarified that generic generated clients require a Gemini signing transport for authenticated requests - **Prediction Markets** - **Sports Market Discovery** - Added the typed `sportsMarket` event classification (`sport`, `type`, `subject`, structured `scope`, and optional `metric`) and repeatable `sport`, `sports_market_type`, `sports_market_subject`, `sports_market_scope`, and `sports_market_metric` filters to all event collection endpoints - **Prediction Markets** - **TypeScript SDK** - Added the unified `GeminiMarkets.predictions` facade with explicit authentication, terms-gated order placement, combo discovery, and rewards access - Added install-from-tarball consumer verification and complete unified-facade package usage guidance - Added a safe-by-default sandbox HMAC smoke workflow with explicit mutation flags and guaranteed order-cancellation cleanup - Corrected sandbox live books to use the Prediction Markets WebSocket host and periodic depth snapshots - Added a sandbox OAuth proof for PKCE or confidential authorization, Bearer-only requests, refresh-token rotation, and revocation - Added a repository-only QA routing harness that loads local gitignored configuration and exercises the built SDK against internal TLS REST and WebSocket targets without exposing staging as a public SDK environment - Added cross-cutting generated REST integration coverage for complete operation manifests, public and authenticated transport routing, interchangeable authentication strategies, and reserved request fields - Completed generated REST surface finalization with package exports, deterministic CI drift detection, packaging verification, and low-level usage guidance ### [2026-07-21](#2026-07-21) - **Prediction Markets** - **REST Order Management** - Documented `from` and `to` epoch-millisecond bounds on `POST /v1/prediction-markets/orders/history`, including inclusive/exclusive range semantics and time-bounded pagination constraints - **Prediction Markets** - **TypeScript SDK** - Added generated authenticated terms and order-management wrappers for eight Prediction Markets REST operations - Added generated authenticated positions and volume wrappers for three Prediction Markets REST operations - Added generated authenticated maker-rebate and liquidity-rewards wrappers for four Prediction Markets REST operations ### [2026-07-20](#2026-07-20) - **Prediction Markets** - **TypeScript SDK** - Added deterministic OpenAPI-generated model and operation contracts with schema-guided bigint normalization for REST `int64` responses - Added generated public event-discovery wrappers for eight Prediction Markets REST operations - Added generated public combo, maker-rate, and liquidity-rewards discovery wrappers for five Prediction Markets REST operations ### [2026-07-17](#2026-07-17) - **Prediction Markets** - **TypeScript SDK** - Added OAuth 2.0 authentication with public-client PKCE, confidential-client code exchange, state validation, caller-owned atomic token persistence, expiration-aware refresh-token rotation, bearer request authentication, and token revocation ### [2026-07-16](#2026-07-16) - **Prediction Markets** - **TypeScript SDK** - Added the `GeminiMarkets` facade for multiplexed live order books over one resilient WebSocket connection, including snapshot/diff routing, reconnect recovery, subscription-error handling, and full teardown - Added HMAC-SHA384 API-key-session authentication with exact payload signing, per-key monotonic nonce isolation, and time-based nonce support ### [2026-07-15](#2026-07-15) - **Prediction Markets** - **REST Order Management** - Added `POST /v1/prediction-markets/order/batch` to place 1-20 orders in one authenticated request - Added `POST /v1/prediction-markets/order/batch/cancel` to cancel 1-20 orders in one authenticated request - Documented reject-whole-batch validation, sequential non-atomic execution, request-order result alignment, and per-entry partial-success responses - **Prediction Markets** - **Sports Tickers** - Documented the **Team Total (`-TT`)** market for FIFA World Cup soccer: single-team regulation-goal over/under lines (`.5` implied, e.g. `ENGO1` = England Over 1.5 goals). Added it to the soccer market table with a full-ticker example. - **API Specifications** - **OpenAPI** - **AsyncAPI** - **llms.txt** - Published stable machine-readable REST, Prediction Markets, and WebSocket specifications with a `/specs/index.json` catalog, compatibility aliases, prominent product-section sidebar links, and `llms.txt` discovery. - **WebSocket APIs** - **AsyncAPI** - Expanded the WebSocket AsyncAPI spec to cover production methods, market data streams, authenticated account streams, contract lifecycle events, combo RFQ surfaces, and handshake authentication metadata with explicit coverage metadata. ### [2026-07-14](#2026-07-14) - **Prediction Markets** - **Combos Request-for-Quote (RFQ)** - Documented the 1-second quoting, 5-second requester-decision, and 1-second maker-confirmation windows with exact deadline behavior - Clarified sealed quote delivery, price-time winner selection, full-size and limit-price eligibility, fresh close-time collateral fallback, and `validUntil` as a logical-close eligibility cutoff - Added the durable authenticated lifecycle event ID (`i`) and at-least-once deduplication guidance - Documented the `0.001` default price tick and maker partial-fill risk during order-book execution ### [2026-07-10](#2026-07-10) - **Prediction Markets** - **Sports Ticker Format** - Added the soccer Correct Score market (`-CS`) for FIFA World Cup: a grid of exact regulation-time scorelines, with contract format `{HOME}{H}{AWAY}{A}` (e.g. `ESP1BEL0`) - **Prediction Markets** - **Combos Request-for-Quote (RFQ)** - Updated the maker integration, quote validity, lifecycle, and account-scoped stream documentation to match the live WebSocket implementation - Removed retired alternate combo-liquidity draft pages and links ### [2026-07-07](#2026-07-07) - **Prediction Markets** - **Combos Request-for-Quote (RFQ)** - Added a new Combos RFQ section documenting the private sealed-bid auction for multi-leg combo contracts: Overview, WebSocket Streams (`requestForQuote`, `requestForQuote@account`, `requestForQuote@session`), Quote Methods (`rfq.submit_quote`, `rfq.withdraw_quote`, `rfq.confirm_quote`), Maker Integration, and Examples - **REST** - **Ticker** & **Order Book** - Corrected the type of decimal price and size fields from `number` to `string` to match the API, which returns them as JSON strings: `Ticker` (`bid`, `ask`, `last`, and the `volume` symbol amounts) and `OrderBookEntry` (`price`, `amount`) - **WebSocket** - **Prediction Markets** - Added a machine-readable AsyncAPI 3.0 spec (`apis/websocket.yaml`) covering the L2 differential-depth path (subscribe envelope, depth snapshot, and depth diff messages); it is the source for generated SDK message types ### [2026-07-06](#2026-07-06) - **WebSocket** - **Stream Reference** & **Message Format** (Trading and Prediction Markets) - Documented the `c` (last trade price) and `C` (last trade size) fields on the Book Ticker stream - Added the `e` (event type) and `m` (maker flag on fills) fields to the Order Events stream - Documented `STOP_LIMIT` and `STOP_MARKET` in the Order Events `o` (type) enum - Added the `P` (stop price) field to the Prediction Markets Order Events stream (already present in the Trading Stream Reference) - Corrected the Balance Updates example `E` / `u` timestamps to nanosecond precision to match the documented field units - Added an **Event Types** reference table to the Message Format page listing the `e` value for each public stream, noting that Book Ticker, Partial Depth, and Trade payloads carry no `e` and that unrecognized event types should be ignored - Clarified the Order Events lifecycle for post-only/immediate time-in-force: `MOC`/`IOC`/`FOK` orders are accepted then cancelled (never `REJECTED`), and a fully-filled `IOC` still ends with a `CANCELED` event — use executed quantity (`Z`), not final status, to determine fills ### [2026-07-02](#2026-07-02) - **Prediction Markets** - **Taker Rewards Program** - Added a link to the [Taker Rewards page](https://exchange.gemini.com/predictions/taker-rewards) to view the remaining reward pool ### [2026-06-30](#2026-06-30) - **Prediction Markets** - **Taker Rewards Program** - Clarified that the Daily Reward equals the applicable Daily Rate multiplied by the qualifying taker volume traded that day; trailing 30-day taker volume determines only the rate tier - Made the Daily Reward and Monthly Bonus volume tier boundaries explicit, non-overlapping ranges aligned with the program terms - Labeled the volume tier columns as "taker volume" to reinforce that only taker-side volume qualifies ### [2026-06-25](#2026-06-25) - **Prediction Markets** - **Ticker Formats** - **Sports** - Added the soccer **To Advance (Winner)** market type (`-A`) for single-leg knockout matches (e.g. World Cup knockout rounds): two contracts, no draw, settled on which team wins after extra time and penalties. The event ticker is the moneyline ticker with `-M` replaced by `-A` - Documented that a single-leg knockout match is listed as both a 3-way moneyline (`-M`, includes draw, regulation only) and a To Advance market (`-A`) ### [2026-06-17](#2026-06-17) - **Prediction Markets** - **Combo Contracts** - Added supplementary developer reference material for combo contracts ### [2026-06-15](#2026-06-15) - **Prediction Markets** - **Taker Rewards Program** - Updated launch date to July 1, 2026 (previously June 22, 2026) with up to $1.5M in taker rewards - Removed the June stub-period Monthly Bonus table; program now runs across July, August, and September 2026 full-month periods - Reworded Reward Examples table column headers and expanded monthly volume values to full numeric form - Updated Program Caps language to "may continue or terminate" the Program if the cap is reached ### [2026-06-11](#2026-06-11) - **Prediction Markets** - **Maker and Taker Incentives** - Added new "Taker Rewards Program" page documenting the 90-day program launching June 22, 2026 — covers Daily Reward tiers, Monthly Bonus tiers (June stub and full-month periods), reward examples, eligibility price band, program caps, and payment terms ### [2026-06-10](#2026-06-10) - **Prediction Markets** - **Maker and Taker Incentives** - Renamed "Market Makers" section to "Maker and Taker Incentives" - Added new "Market Maker Program" page describing the application-based program for approved market makers providing liquidity across contract categories ### [2026-06-08](#2026-06-08) - **Prediction Markets** - **Ticker Formats** - **Sports** - Added World Cup (`FIFAWC`) and International Friendlies (`INTLFRIENDLY`) to the supported soccer leagues, with moneyline (3-way, incl. Draw), spread, and total ticker formats plus worked examples. `INTLFRIENDLY` reuses the `FIFAWC` national-team roster ### [2026-06-05](#2026-06-05) - **Prediction Markets** - **REST APIs** - **Positions** - `POST /v1/prediction-markets/positions`: added `positionValue` to the `sort` enum (with `+`/`-` prefix variants); previously omitted despite being a valid value - `POST /v1/prediction-markets/positions`: corrected `sort` semantics — bare field defaults are per-field (`positionValue` and `unrealizedPnl` descending; `expiryDate` ascending, soonest-first). NULLS LAST sinking applies to both `unrealizedPnl` and `expiryDate`. Malformed values silently fall back to `-positionValue` - `POST /v1/prediction-markets/positions`: clarified `eventTicker` may also match positions on sub-events whose `parentEventTicker` equals the value; `offset` is ignored when `limit` is omitted - `POST /v1/prediction-markets/positions`: clarified `unrealizedPct` units — value is a percent (e.g. `12.5` = 12.5%, **not** `0.125`), rounded to 4 decimal places ### [2026-06-02](#2026-06-02) - **WebSocket** - **Prediction Markets WebSocket** - Documented the `c` (confirmed balance) field in the Balance Updates stream — represents the total balance including pending amounts; `f` (available balance) is the amount available to trade ### [2026-06-01](#2026-06-01) - **WebSocket** - **Prediction Markets WebSocket** - Documented the `n` (fee amount) field in the Order Events stream `fill` object — present only on `FILLED` events, applies to spot, prediction markets, and PERPS ### [2026-05-29](#2026-05-29) - **OAuth 2.0** - **Public Clients** - Documented public clients and the required [PKCE](/authentication/oauth#public-clients-and-pkce) flow for native, desktop, and single-page apps — `code_verifier`/`code_challenge` parameters, the `S256` requirement, mandatory `state`, and loopback redirect URIs ### [2026-05-28](#2026-05-28) - **OAuth 2.0** - **Prediction Markets** - Added prediction market REST endpoints to the OAuth scopes table - Added OAuth 2.0 bearer token authentication option to Prediction Markets WebSocket docs ### [2026-05-27](#2026-05-27) - **REST APIs** - **Common** - **Admin** - Added [Subaccounts](/rest-api/common/admin/subaccounts) overview — account hierarchy, API key mechanics, and patterns for agentic trading, prediction market bots, and team/client isolation ### [2026-05-26](#2026-05-26) - **WebSocket** - **Authentication** - Documented that the WebSocket API accepts OAuth 2.0 bearer tokens in the `Authorization` header during the connection upgrade, in addition to HMAC-signed API keys - **Prediction Markets** - **REST APIs** - **Positions** - `POST /v1/prediction-markets/positions`: added optional query params `eventTicker`, `limit`, `offset`, and `sort` (values: `unrealizedPnl`, `expiryDate`, each acceptable with `+`/`-` prefix or bare = descending). Omitting all params preserves the legacy unpaginated, unsorted behavior - `POST /v1/prediction-markets/positions`: added per-position fields `marketValue`, `unrealizedPnl`, and `unrealizedPct`. All three are **absent** from the response (field omitted from the JSON object) when the held outcome has no live sell quote — partners should render a no-liquidity state rather than a price the user cannot transact at. `lastTradePrice` is still returned for display. `unrealizedPnl` sorts NULLS LAST with `instrumentId` as the final tiebreaker; malformed `sort` values silently fall back to the default order (no `400`). Partner clients should treat the three fields as `Optional` not `T | null` - `POST /v1/prediction-markets/positions/settled`: added optional query params `limit`, `offset`, `sort` (values: `date`, `payout`), `search` (case-insensitive substring; 3-char floor, 64-char cap), and `category` (filters by event category, including descendants). `date` ascending is rejected and falls back silently to default order - `POST /v1/prediction-markets/positions/settled`: added optional `withCashOuts` query param (default `false`). When `true`, the response carries new sibling fields `cashOuts`, `totalCashOutProceeds`, `totalCashOutCostBasis`, and `totalCashOutNetProfit`. The `positions[]` element schema is unchanged regardless of the flag — strictly additive - `POST /v1/prediction-markets/positions/settled`: `totalPayout`, `totalCostBasis`, and `totalNetProfit` are now **absent** (field omitted from the JSON object — not `null`) on the new unified backend. Field keys remain reserved on the response schema for binary back-compat; partner clients should treat them as `Optional` not `T | null` - Added `CashedOutPosition` schema (fields: `accountId`, `instrumentId`, `instrumentSymbol`, `timestamp`, `filledQuantity`, `side`, `proceeds`, `costBasis`, `netProfit`, `contractMetadata`) — exposed only via the `withCashOuts=true` sibling array ### [2026-05-23](#2026-05-23) - **Prediction Markets** - **Liquidity Rewards Program** - Clarified that Spread weight uses the best price on your side as the reference when the book is one-sided - **WebSocket** - **Position Updates** - Documented the `positions@account@1s` stream — periodic 1-second snapshot of all open event-contract positions for the authenticated account; reuses the existing `positionReport` wire shape ### [2026-05-22](#2026-05-22) - **Prediction Markets** - **Liquidity Rewards Program** - Added link to the Liquidity Rewards page on Gemini Exchange in the program overview - Linked the Liquidity Rewards REST API endpoints (Get Config, List Events, Get Daily Summary, Get Lifetime Summary) in the program overview callout - **Prediction Markets** - **REST APIs** - **Rewards** - Linked "Liquidity Rewards" in the Rewards category page description to the Liquidity Rewards page on Gemini Exchange - **Styles** - Added vertical spacing between adjacent paragraphs inside `.exchange-link-callout` ### [2026-05-21](#2026-05-21) - **Prediction Markets** - **REST APIs** - **Rewards** - Added REST API reference pages for the Maker Rebate program: `GET /v1/prediction-markets/maker-rebate/rates` (Get Rate Schedule), `POST /v1/prediction-markets/maker-rebate/payouts` (List Payouts), and `GET /v1/prediction-markets/maker-rebate/summary/total` (Lifetime Summary) - Added REST API reference pages for the Liquidity Rewards program: `GET /v1/prediction-markets/liquidity-rewards/config` (Program Config), `GET /v1/prediction-markets/liquidity-rewards/events` (List Events), `GET /v1/prediction-markets/liquidity-rewards/summary/daily` (Daily Summary), and `GET /v1/prediction-markets/liquidity-rewards/summary/total` (Lifetime Summary) ### [2026-05-20](#2026-05-20) - **Prediction Markets** - **Combo Contracts** - Added Combo Contracts section with overview covering contract specification, ticker format, pricing, settlement state machine, orderbook behavior, discovery, and FAQ - Added REST API reference pages for `GET /v1/prediction-markets/combos` (List Combos) and `GET /v1/prediction-markets/combos/{instrumentSymbol}` (Get Combo) ### [2026-05-18](#2026-05-18) - **Prediction Markets** - **REST APIs** - Added terms endpoints for API key and OAuth flows: `GET /v1/prediction-markets/terms`, `GET /v1/prediction-markets/terms/status`, and `POST /v1/prediction-markets/terms/accept` ### [2026-05-15](#2026-05-15) - **WebSocket** - **Navigation** - Added Introduction page to both Trading and Prediction Markets WebSocket sections ### [2026-05-14](#2026-05-14) - **Prediction Markets** - **Market Makers** - Added Liquidity Rewards Program documentation - **Prediction Markets** - **Getting Started** - Consolidated the Prediction Markets intro and getting-started flow into the main Prediction Markets landing page - Added beginner market-maker and agent workflow guidance, including public market discovery, quoting loop, guardrails, first-order example, and common first-run issues - Added guidance for computing notional dollars from WebSocket depth snapshots and depth updates instead of relying on a separate dollar field - Clarified that WebSocket is the preferred path for active trading and market making, while REST is used for event discovery, account snapshots, and reconciliation - Added REST-vs-WebSocket order payload guidance, first-order prerequisites, symbol glossary, and beginner market-maker terminology - Clarified maker-only REST order behavior with the `makerOrCancel` field and aligned position examples to the web-api `POST /v1/prediction-markets/positions` route - Updated internal links to use canonical Prediction Markets REST and WebSocket routes - **Prediction Markets** - **WebSocket Streams** - Added a stream matrix covering public market data, authenticated account streams, and contract lifecycle streams - Clarified local order book maintenance, YES-space depth calculations for YES and NO notional, and REST reconciliation after `positions@account` reconnects or settlement windows - Re-added `positions@account` stream documentation for real-time event-contract position updates, including authentication requirements, subscribe and acknowledgement examples, and the `positionReport` wire shape - Updated Prediction Markets WebSocket examples and interactive tools to use `wss://ws.gemini.com` and include `positions@account` for exposure updates ### [2026-05-12](#2026-05-12) - **Prediction Markets** - **REST APIs** - Added documentation pages for the following endpoints: Get Strike Price for Event (`GET /v1/prediction-markets/events/{eventTicker}/strike`), List Newly Listed Events (`GET /v1/prediction-markets/events/newly-listed`), List Recently Settled Events (`GET /v1/prediction-markets/events/recently-settled`), List Upcoming Events (`GET /v1/prediction-markets/events/upcoming`), Get Order History (`POST /v1/prediction-markets/orders/history`), Get Settled Positions (`POST /v1/prediction-markets/positions/settled`), and Get Volume Metrics (`POST /v1/prediction-markets/metrics/volume`) - **Prediction Markets** - **WebSocket Streams** - Removed `positions@account` stream documentation (stream removed) ### [2026-05-08](#2026-05-08) - **WebSocket** - **Playground** - Fixed sidebar navigation disappearing when navigating to Playground pages under Trading and Prediction Markets WebSocket sections - Removed the empty "Overview" entry from the Playground sidebar; it was always highlighted because it shared a path with the page itself, masking the active method ### [2026-05-07](#2026-05-07) - **Prediction Markets** - **WebSocket Streams** - Documented the `positions@account` stream for real-time event-contract position updates: snapshot-then-delta semantics, sign convention (negative value = short), zero-position eviction (delta emits-then-evicts; repeat zeros suppressed), and terminal settlement details in the final position row - Added the `positionReport` event payload (top-level `e/E/u/A/P` and per-row `t/s/a[]` with each amount's `t/v/c`) ### [2026-05-06](#2026-05-06) - **Infrastructure** - Upgraded Zudoku from 0.66.1 to 0.76.0 (smaller bundle, OpenAPI playground array-parameter support, miscellaneous fixes) ### [2026-05-05](#2026-05-05) - **Trading** - **Fund Management** - Moved Fund Management endpoints under Trading → REST APIs to match the new vertical structure (16 endpoints: balances, deposits, withdrawals, payment methods, approved addresses, transfers, transaction history) ### [2026-05-01](#2026-05-01) - **Prediction Markets** - **Maker Rebate Program** - Removed outdated April 9, 2026 promotional offer notice ### [2026-04-29](#2026-04-29) - **Prediction Markets** - **Maker Rebate Program** - Extended promotional period end date to June 10, 2026 (post-promotional rates effective June 11, 2026+) - Added Politics category (0.70 rebate rate) and Economics category (0.50 rebate rate) effective May 10, 2026 - Sports rebate rate remains 0.50 in post-promotional period ### [2026-04-24](#2026-04-24) - **Prediction Markets** - **WebSocket Streams** - Documented the `contractStatus` stream under Prediction Markets → WebSocket → Streams, covering lifecycle events (status transitions) and strike-populated moments for Up/Down contracts - Added `p` (strike price) field, parsed from the contract ticker and omitted for Up/Down contracts until the strike is set - Added a "Contract Status" entry to the prediction-markets Streams sidebar - **Prediction Markets** - **Positions** - Documented the `POST /v1/prediction-markets/positions/settled` endpoint for retrieving historically settled positions, including `payout`, `resolutionSide`, `costBasis`, `realizedPnl`, and `netProfit` fields, with optional `eventTicker` filter ### [2026-04-23](#2026-04-23) - **WebSocket** - **Sidebar Navigation** - Added a "Contract Status" entry under Streams so the `contractStatus` stream is reachable from the websocket sidebar ### [2026-04-22](#2026-04-22) - **WebSocket** - **Contract Status Stream** - Documented the `contractStatus` stream for prediction-market contract lifecycle events (status transitions and strike-populated moments) - Added `p` (strike price) field, parsed from the contract ticker and omitted for Up/Down contracts until the strike is set ### [2026-04-17](#2026-04-17) - **Prediction Markets** - **Trading** - Added `stop-limit` to the supported `orderType` values for `POST /v1/prediction-markets/order` - Added `stopPrice` field to the order request and response schemas (required when `orderType` is `stop-limit`) - **WebSocket** - **order.place** - Clarified that supplying `stopPrice` with `type: "LIMIT"` places a stop-limit order (activates as a limit once the trigger is reached). Documented BUY/SELL price constraints. ### [2026-04-15](#2026-04-15) - **Market Data** - **Get Assets for Network** - Changed `/v2/networks/{network}/assets` endpoint from public to authenticated; now requires API key with Fund Manager or Auditor role ### [2026-04-09](#2026-04-09) - **Prediction Markets** - **Maker Rebate Program** - Updated promotional rebate rates start date to April 9, 2026; promotional period runs April 9 - May 9, 2026 - Simplified rebate rates table to show promotional and post-promotional periods only ### [2026-04-08](#2026-04-08) - **Prediction Markets** - **Crypto Tickers** - Updated 5-minute and 15-minute contract examples to use `UP` contract type instead of `HI{PRICE}` - Added `UP` contract documentation for short-duration contracts where strike price is returned via API response - Legacy `HI{PRICE}` format noted as deprecated for short-duration contracts ### [2026-04-07](#2026-04-07) - **Prediction Markets** - **Positions Endpoint** - Added new position fields: `quantityOnHold`, `prices`, `resolutionSide`, `isAboveAutoStartThreshold`, `isLive`, `realizedPl` - Added new `prices` sub-object with buy/sell prices for yes/no outcomes, plus `bestBid`, `bestAsk`, and `lastTradePrice` - Added new contract metadata fields: `eventImageUrl`, `eventType`, `resolutionSide`, `sortOrder`, `parentEventTicker`, `template`, `color`, `startTime` - Added `total` field to response for pagination support ### [2026-04-04](#2026-04-04) - **/v1/balances** - Clarified that the `amount` field (confirmed balance) is not reduced until the withdrawal has been confirmed on the blockchain, as a safeguard against blockchain reorganizations - Clarified that the `available` field is reduced immediately when an order hold or withdrawal hold is placed, making it the recommended field for tracking real-time spendable balances - Recommend using `showPendingBalances: true` for explicit `pendingDeposit`/`pendingWithdrawal` visibility (note: slower response due to additional database lookup) ### [2026-04-01](#2026-04-01) - **Trading** - Added WebSocket quickstart section to the Trading landing page with Python and Node.js code samples for connecting to `wss://ws.gemini.com`, streaming real-time prices, placing orders, and handling fill notifications ### [2026-03-31](#2026-03-31) - **Prediction Markets** - **Getting Started** - Removed US-only trading restriction callout - **Prediction Markets** - **Maker Rebate Program** - Updated program status to reflect that the Maker Rebate Program is now live - Updated April 10 and May 10 dates to April 9 and May 9 ### [2026-03-30](#2026-03-30) - **Prediction Markets** - **Sports Tickers** - Added Individual Sports section (Golf, Formula 1) with tournament/race-based ticker format - **Prediction Markets** - **Strike Endpoint** - Added `GET /v1/prediction-markets/events/{eventTicker}/strike` endpoint to retrieve strike price for a specific event - Returns strike `value`, `type`, and `availableAt` for crypto Up/Down contracts ### [2026-03-27](#2026-03-27) - **Prediction Markets** - **Maker Rebate Program** - Added promotional rebate rates effective April 10, 2026 - May 10, 2026 for Crypto and Commodities (0.70), Sports (0.50), and All other markets (0.30) - **Prediction Markets** - **Weather Tickers** - Added Weather Ticker Format specification for highest temperature prediction markets - Supports five locations: NYC, MDW, MIA, LAX, BOS with LO, range, and HI contract types - **Prediction Markets** - **Crypto Up/Down Contracts** - Added `strike` object to Contract schema with `value`, `type`, and `availableAt` fields for Up/Down contracts - Added `source` field to Contract and Event schemas for data source identifier (e.g., `GRR-KAIKO_BTCUSD_60S`) - Added `settlementValue` field to Contract schema for observed settlement price - Added `settlement` object to Event schema with observed value at expiry ### [2026-03-26](#2026-03-26) - **Prediction Markets** - **Commodities Tickers** - Added [Commodities Ticker Format](/prediction-markets/tickers-commodities) documentation for commodity price prediction markets - Supported commodities: Natural Gas (`NGAS`), Oil (`WTI`, `BRENT`), Copper (`COPPER`), Silver (`XAG`), Gold (`XAU`) ### [2026-03-25](#2026-03-25) - **Prediction Markets** - **Trading** - Added `maker-or-cancel` option to `timeInForce` parameter for `POST /v1/prediction-markets/order` - Maker-or-cancel orders only add liquidity to the order book; if any part would fill immediately, the entire order is cancelled (also known as "post-only") - Useful for ensuring orders qualify for [maker rebates](/prediction-markets/maker-rebate-program) - **Fund Management** - Added v2 fee estimation endpoint `POST /v2/withdraw/\{network\}/\{ticker\}/feeEstimate` with explicit blockchain network support for multi-network tokens - Added v2 withdraw endpoint `POST /v2/withdraw/{network}/{ticker}` with explicit network selection for multi-chain token withdrawals - Added network-aware v2 transfers endpoint `POST /v2/transfers` with full multichain support for deposits and withdrawals - **GET /v2/networks/\{network\}/assets** - Added new v2 reverse asset lookup endpoint for discovering which tokens are available on a given blockchain network - Returns alphabetically sorted array of asset codes with support for all enabled networks - **GET `/v2/network/{token}`** - Added authenticated v2 network endpoint that returns available networks filtered by account-level deposit and withdraw access - **WebSocket API** - **Connection Parameters** - Added documentation for the `cancelOnDisconnect` connection-level query parameter - When enabled (`?cancelOnDisconnect=true`), all open orders are automatically cancelled when the WebSocket session disconnects ### [2026-03-21](#2026-03-21) - **Prediction Markets** - **Crypto Tickers** - Updated HI contract description from "greater than" to "greater than or equal to" to accurately reflect the `>=` threshold behavior ### [2026-03-16](#2026-03-16) - **/v1/balances** - Updated examples to use `showPendingBalances: false` as the default - Added note that setting `showPendingBalances` to `true` results in slower response times due to additional database lookups - Added `_timestamp` field to the balance response schema — a monotonically increasing server-side clock in nanoseconds, allowing clients to detect and filter out stale responses ### [2026-03-13](#2026-03-13) - **Prediction Markets** - **Getting Started** - Updated getting started guide with WebSocket-first trading examples in Node.js and Python - **WebSocket API** - **Connection Parameters** - Added documentation for the `snapshot` connection-level query parameter - The `snapshot` parameter controls initial orderbook snapshot delivery when subscribing to differential depth streams - Supports full snapshot (`-1`), top N levels (positive integer), or no snapshot (`0`, default) - **Prediction Markets** - **Maker Rebate Program** - For makers and takers feed fixed links so the page jumps to the specific section - **Prediction Markets** - **Navigation** - Flattened prediction markets page structure to fix sidebar navigation links - Added URL redirects for backward compatibility with old nested paths - **llms.txt** - Fixed llms.txt generation to exclude internal Zudoku routes (`~endpoints`, `~schemas`) from sitemap-derived links ### [2026-03-12](#2026-03-12) - **Prediction Markets** - **Maker Rebate Program** - Added Maker Rebate Program documentation with rebate formula, rates, eligibility, examples, and payment details - Rebate rates table now includes period column - Rebate examples table now includes Taker Rate, Rebate Rate, and Maker Rebate columns - Rebate precision changed from rounded up to rounded down - Simplified eligibility section - Clarified maker order definition in overview - Added links to fee schedule in rebate examples table headers - Bolded linked column headers in rebate examples table for consistency - **Prediction Markets** - **Getting Started** - Removed "US Only" callout from Getting Started page ### [2026-03-04](#2026-03-04) - **Prediction Markets** - **Discovery Endpoints** - Added `GET /v1/prediction-markets/events/newly-listed` for events created in the last 24 hours - Added `GET /v1/prediction-markets/events/recently-settled` for events settled in the last 24 hours - Added `GET /v1/prediction-markets/events/upcoming` for approved pre-launch events - All discovery endpoints support `category` filtering and pagination (`limit`/`offset`) - **Prediction Markets** - **Contract Pricing** - Replaced `price` field with `prices` object containing `buy`, `sell`, `bestBid`, `bestAsk`, and `lastTradePrice` - Added `abbreviatedName`, `marketState`, and `sortOrder` fields to Contract schema - Added `Subcategory` schema with `id`, `slug`, `name`, and `path` fields - Added `approved` status to `MarketStatus` enum - Updated max `limit` parameter from 100 to 500 - **Prediction Markets** - **Volume Metrics** - Added `POST /v1/prediction-markets/metrics/volume` endpoint for per-contract share volume metrics - Returns total volume, user taker (aggressor) volume, and user maker (resting) volume per contract - Supports optional time range filtering via `startTime` and `endTime` parameters - **Prediction Markets** - **Tickers** - Corrected sports ticker prefix from `GEM-` to `GEMI-` ### [2026-02-25](#2026-02-25) - **Prediction Markets** - **Crypto Ticker Format** - Added duration markers for 5-minute and 15-minute interval crypto contracts - 5-minute contracts now use format: `BTC05M2602251745` (with `05M` marker) - 15-minute contracts now use format: `BTC15M2602251745` (with `15M` marker) - All other durations continue to use format without duration marker: `BTC2603230800` - Updated full ticker examples, regex patterns, and validation rules ### [2026-02-24](#2026-02-24) - **WebSocket API** - Updated public WebSocket URL from `wss://wsapi.fast.gemini.com` to `wss://ws.gemini.com` ### [2026-02-22](#2026-02-22) - **WebSocket Documentation** - Renamed "Fast API" references to "WebSocket API" for clarity and consistency across introduction, authentication, message format, and stream reference pages ### [2026-02-18](#2026-02-18) - **WebSocket Documentation** - Updated contact information for WebSocket high performance tier onboarding ### [2026-02-18](#2026-02-18) - **WebSocket Documentation** - Fixed broken internal links: updated all remaining `/websocket/fast-api/*` references to `/websocket/*` across documentation pages, config, and plugins ### [2026-02-17](#2026-02-17) - **WebSocket API** - Updated onboarding instructions for WebSocket high performance tiers ### [2026-02-16](#2026-02-16) - **WebSocket Documentation** - Simplified URL structure: WebSocket documentation moved from `/websocket/fast-api/*` to `/websocket/*` - Updated paths: `/websocket/fast-api/introduction` → `/websocket/introduction` (and similar for all WebSocket pages) - Maintained backward compatibility with `/fast-api` redirect ### [2026-02-15](#2026-02-15) - **WebSocket API** - Updated Fast API status from Production Beta to Production - Archived old WebSocket API v1/v2, Order Events, and Multi Market Data documentation under the Archived tab - Fixed broken links to removed WebSocket documentation across the site ### [2026-02-14](#2026-02-14) - **FastAPI** - **Message Format** - **Stream Reference** - Fixed `error.code` type from string to number in message format documentation - Added missing `OPEN` and `MODIFIED` order statuses to order events - Added `O` (eventOutcome) field to order events for event contracts - Added timestamp units (nanoseconds/milliseconds) to all stream field tables - Added error codes reference table with all status codes - Added rejection and cancellation reason tables for order events - Added note that order event fields with empty values may be omitted - Updated `id` field type to `string | number` across request/response docs - Updated interactive API fallback spec to match live api.json (v0.10.10) - Clarified the `Z` (Executed quantity) field description to explain its different meanings depending on event type: last fill quantity for `FILLED` / `PARTIALLY_FILLED` events, cumulative filled quantity for `CANCELED` and other events. ### [2026-02-13](#2026-02-13) - **Prediction Markets** - Added [Ticker Format](/prediction-markets/tickers-overview) specifications for crypto and sports prediction markets - **FastAPI balances@account@1s Stream** - Documentation for new `balances@account@1s` FastAPI websocket stream that sends periodic balance snapshots every second. ### [2026-02-02](#2026-02-02) - **Prediction Markets REST API** - Updated `MarketStatus` enum value from `underreview` to `under_review` for consistency with snake_case naming convention ### [2026-01-23](#2026-01-23) - **GET `/v1/network/{token}`** - Updated documentation to clarify multi-network support for tokens - Added example showing USDC available on multiple networks (Optimism, Solana, Base, Arbitrum, Monad, Avalanche, Ethereum) - Expanded list of supported networks in schema description ### [2026-01-21](#2026-01-21) - **Order Events API Update** - Added one-liner to add stop price as a field ### [2026-01-14](#2026-01-14) - **/v1/balances showPendingBalances Parameter** - Documentation for optional `showPendingBalances` parameter for /v1/balances endpoint. ### [2026-01-14](#2026-01-14) - **FastAPI balances@account Stream** - Documentation for new `balances@account` FastAPI websocket stream. ### [2026-01-08](#2026-01-08) - **FIX Dictionary** - Added `7777 EventOutcome` FIX tag for prediction markets orders. ### [2025-12-16](#2025-12-16) - **Transfer History** - `POST /v1/transfers` - `show_completed_deposit_advances` default value updated to True. ### [2025-12-15](#2025-12-15) - **WebSocket API** - Updated WebSocket overview introduction to highlight Fast API as next-generation solution - Added recommendation for new integrations to start with Fast API - Added cross-references between traditional WebSocket APIs and Fast API - **Prediction Markets REST API** - Added new Prediction Markets API documentation section with complete examples - **Discovery endpoints (public):** - `GET /v1/prediction-markets/events` - List prediction market events with filtering - `GET /v1/prediction-markets/events/{eventTicker}` - Get event details by ticker - `GET /v1/prediction-markets/categories` - List available event categories - **Trading endpoints (authenticated):** - `POST /v1/prediction-markets/order` - Place a prediction market limit order (only limit orders supported) - `POST /v1/prediction-markets/order/cancel` - Cancel an existing order - **Position endpoints (authenticated):** - `POST /v1/prediction-markets/orders/active` - Get active orders - `POST /v1/prediction-markets/orders/history` - Get order history - `POST /v1/prediction-markets/positions` - Get current positions - **Market data:** Use existing REST and WebSocket market data endpoints with prediction market contract symbols (e.g., `GEMI-BTC100K-YES`) - **Documentation enhancements:** - Added comprehensive request/response examples to all endpoints - Created Getting Started guide with complete workflow walkthrough and Python code examples - Added examples for order placement (buy/sell), active orders, events discovery, and positions ### [2025-11-20](#2025-11-20) - **FIX Order Entry - Tag 544 (CashMargin) Restrictions** - Updated documentation for tag 544 (CashMargin) field restrictions across different order types - **Tag 544 Support Matrix:** | Order Type | Buy Side (Side=1) | Sell Side (Side=2) | Error Message (if not supported) | | ---------- | ----------------- | ------------------ | -------------------------------------------------------- | | Market | ✅ Supported | ❌ Not Supported | "CashMargin \<544\> not supported for sell orders" | | Limit | ✅ Supported | ❌ Not Supported | "CashMargin \<544\> not supported for sell orders" | | Stop-Limit | ❌ Not Supported | ❌ Not Supported | "CashMargin \<544\> not supported for stop-limit orders" | - **Key Points:** - Stop-limit orders: Tag 544 is completely unsupported regardless of side - Market/Limit orders: Tag 544 is only supported for buy-side orders (Side=1) - Sell-side restriction: Any sell order (Side=2) with tag 544 will be rejected - Clients should only include tag 544 for buy-side market or limit orders ### [2025-11-18](#2025-11-18) - **Margin Trading** - Added `/v1/margin/account` endpoint to retrieve margin account summary and risk statistics - Added `/v1/margin/rates` endpoint to retrieve current margin interest rates - Added `/v1/margin/order/preview` endpoint to preview margin impact of spot orders ### [2025-10-31](#2025-10-31) - **Mark Price WebSocket API** - Updated mark price documentation to also support select spot pairs. ### [2025-10-07](#2025-10-07) - **Fast API WebSocket API** - Updated trade stream documentation to remove Order ID, Trade Time, and Side from the event message. ### [2025-10-01](#2025-10-01) - **Fast API WebSocket API** - Created initial documentation for the new Fast API ### [2025-09-10](#2025-09-10) - **REST** - Updated `/v1/account` to include optional `virtual_account_number` field in response ### [2025-07-10](#2025-07-10) - **REST** - Updated `/v1/balances` to reflect new response fields `pendingWithdrawal` and `pendingDeposit` ### [2025-05-30](#2025-05-30) - **REST** - Updated `/v1/balances` to only accept a single account ### [2025-05-20](#2025-05-20) - **REST** - **Websocket** - **FIX Market Data** - **FIX Order Entry** - **FIX Drop Copy** - Improve API docs website ### [2025-05-19](#2025-05-19) - **REST** - Remove Documentation for `/v1/approvedAddresses/:network/request` ### [2025-03-14](#2025-03-14) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support `ARB` - Documentation for new token support `RLUSD` ### [2025-02-10](#2025-02-10) - **REST** - Remove Documentation for delisted perps support `MATIC` ### [2025-01-28](#2025-01-28) - **REST** - **Websocket** - **FIX Market Data** - Remove Documentation for delisted token: `LTCBCH`, `BCHETH`,`BCHBTC` ### [2025-01-21](#2025-01-21) - **REST** - Documentation for new perps support `TRUMP` ### [2024-12-26](#2024-12-26) - **REST** - **Websocket** - Documentation for new token support `CHILLGUY` ### [2024-12-10](#2024-12-10) - **REST** - Documentation for new perps support `SHIB`, `UNI`, `BCH` ### [2024-11-27](#2024-11-27) - **REST** - **Websocket** - Documentation for new token support `FLOKI`, `PYTH` ### [2024-11-19](#2024-11-19) - **REST** - **Websocket** - Documentation for new token support `PNUT`, `GOAT`, `MEW`, `BOME` ### [2024-11-15](#2024-11-15) - **REST** - **Websocket** - Documentation for new token support `MOODENG` ### [2024-11-04](#2024-11-04) - **REST** - **Websocket** - Documentation for new token support `BONK`, `POPCAT`, `OP` ### [2024-09-20](#2024-09-20) - **REST** - **Websocket** - **FIX Market Data** - Remove Documentation for delisted token: `LUNA`,`SNX`,`QRDO`,`ZBC` ### [2024-09-18](#2024-09-18) - **REST** - **Websocket** - **API Change** Remove Documentation for support `/v1/balances/earn`, `/v1/earn/rates`, `/v1/earn/interest` - Documentation for new token support `WIF` ### [2024-05-21](#2024-05-21) - **Websocket** - Adding 'tid' to the TRADE RESPONSE events under [Market Data v2](/websocket/market-data-v2/index) ### [2024-05-05](#2024-05-05) - **REST** - **Websocket** - Remove Documentation for delisted token: `OXT-BTC`,`OXT-ETH`,`BAT-BTC`,`BAT-ETH`, `BTC-DAI`, `ETH-DAI` ### [2024-04-23](#2024-04-23) - **Websocket** - **FIX Market Data** - Remove Documentation for delisted token: `ZEC` ### [2024-04-05](#2024-04-05) - **REST** - Documentation for new perps support `WIF` ### [2024-03-04](#2024-03-04) - **FIX Market Data** - **New Feature:** Add FundingAmount support ### [2024-02-29](#2024-02-29) - **REST** - Documentation for new perps support `MATIC`, `DOGE`, `LINK`, `AVAX`, `LTC`, `DOT` ### [2024-02-06](#2024-02-06) - **REST** - Documentation for new perps support `XPR`, `SOL` ### [2023-11-15](#2023-11-15) - **REST** - **Websocket** - **FIX Market Data** - Remove Documentation for delisted token: `MIR`, `UST`, `FXS`, `FRAX`, `BUSD` ### [2023-09-18](#2023-09-18) - **REST** - **Websocket** - **FIX Market Data** - Removed Documentation for new token support: `MPL`, `MC`, `METIS`, `RBN`, `GFI`, `LQTY`, and `LUSD` ### [2023-09-11](#2023-09-11) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `HNT` ### [2023-08-10](#2023-08-10) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `XRP` ### [2023-08-04](#2023-08-04) - **REST** - **Websocket** - **FIX Market Data** - Documentation for token delist: `ENJ` ### [2023-05-29](#2023-05-29) - **REST** - Updated /v1/order/status to specify orderId as negative ### [2023-05-23](#2023-05-23) - **REST** - Removed: Fund Management APIs -> SEN Withdrawals ### [2023-05-09](#2023-05-09) - **REST** - **Websocket** - Documentation for new token support: `PEPE` ### [2023-05-08](#2023-05-08) - **FIX Market Data** - Corrected documentation (symbol) for BTC-GUSD-PERP ### [2023-02-16](#2023-02-16) - **FIX Market Data** - **New Feature:** Add MarkPrice support ### [2023-01-19](#2023-01-19) - **REST** - Updated json response for [Transfers](/rest/fund-management#list-past-transfers) to include type:`Reward` and method:`CreditCard` ### [2023-01-10](#2023-01-10) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `ATOM`, `USDT` ### [2022-11-03](#2022-11-03) - **REST** - **New Feature** Documentation for [Gemini Staking](/rest/gemini-staking) ### [2022-10-21](#2022-10-21) - **FIX Order Entry** - **New Feature:** Add fix tag 9000 RiskLiquidityFlag. Indicates whether or not the order should match against Liquidation Orders sent from the Liquidation Engine. Only allowed from permissioned Market Makers. ### [2022-10-11](#2022-10-11) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `AVAX` ### [2022-10-03](#2022-10-03) - **REST** - Remove Gemini Dollar section ### [2022-09-14](#2022-09-14) - **REST** - **New Feature** Documentation for [Transactions](/rest/fund-management#get-transaction-history) and [Clearing Trades](/rest/clearing#list-clearing-trades) ### [2022-09-07](#2022-09-07) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `BUSD` ### [2022-08-23](#2022-08-23) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `BICO`, `IMX`, `PLA`, `IOTX` ### [2022-08-01](#2022-08-01) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `GAL`, `EUL`, `SAMO` ### [2022-07-06](#2022-07-06) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `DOT`, `ERN` ### [2022-06-29](#2022-06-29) - **REST** - **API Change** Adding `symbol` parameter to [Get Notional Volume](/rest/orders#get-notional-trading-volume) ### [2022-06-23](#2022-06-23) - **REST** - **Websocket** - **FIX Order Entry** - **FIX Drop Copy** - Deprecating documentation for Auction and Block trading support ### [2022-06-22](#2022-06-22) - **REST** - **API Change** Adding `since_tid` parameter to [Trade History](/rest/market-data#list-trades) - Documentation for new token support: `GUSDGBP` - **Websocket** - Documentation for new token support: `GUSDGBP` ### [2022-06-15](#2022-06-15) - **REST** - Documentation improvement to json example for [Symbol Details](/rest/market-data#get-symbol-details) - **New Feature** Documentation for [Gas Fee Estimation](/rest/fund-management#get-gas-fee-estimation) ### [2022-06-14](#2022-06-14) - **REST** - **API Change** Removed parameter `client_order_id` from the trades array of [order-status](/rest/orders#get-order-status) - Documentation for new token support: `ALI`, `TRU` - **Websocket** - **FIX Market Data** - Documentation for new token support: `ALI`, `TRU` ### [2022-06-07](#2022-06-07) - **REST** - **New Feature** Documentation for [FX Rates](/rest/market-data#fx-rate) ### [2022-06-01](#2022-06-01) - **REST** - **New Feature** Documentation for [Renaming an Account](/rest/account-administration#rename-account) ### [2022-05-25](#2022-05-25) - **REST** - **New Feature** Documentation for [Adding A Bank CAD](/rest/fund-management#add-bank-cad) ### [2022-05-18](#2022-05-18) - **REST** - **New Feature** Documentation for [Clearing Order List](/rest/clearing#list-clearing-orders), [Clearing Broker List](/rest/clearing#list-clearing-brokers) and [Custody Account Fees](/rest/fund-management#list-custody-fee-transfers) - **FIX Order Entry** - **FIX Market Data** - **FIX Drop Copy** - Add additional examples of using the API ### [2022-05-17](#2022-05-17) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `GFI`, `ORCA` ### [2022-05-09](#2022-05-09) - **FIX Order Entry** - Update FIX Order Cancel Reject details ### [2022-04-27](#2022-04-27) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `METIS`, `QRDO`, `ZBC`, `CHZ`, `REVV`, `JAM`, `FIDA`, `GMT` ### [2022-04-26](#2022-04-26) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `GUSDSGD` ### [2022-03-29](#2022-03-29) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `RBN`, `FXS`, `DPI`, `LQTY`, `LUSD`, `FRAX`, `INDEX`, `MPL` ### [2022-03-16](#2022-03-16) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `APE` ### [2022-03-01](#2022-03-01) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `RAY`, `SBR` ### [2022-02-28](#2022-02-28) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `SOL` ### [2022-02-08](#2022-02-08) - **REST** - **API Change** Added new parameter `clientTransferId` to [Transfer Between Accounts](/rest/fund-management#transfer-between-accounts) and [Withdraw Crypto Funds](/rest/fund-management#withdraw-crypto-funds) ### [2022-02-01](#2022-02-01) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `TOKE`, `LDO`, `RLY` ### [2022-01-28](#2022-01-28) - **REST** - **New Feature** Documentation for Earn History ### [2022-01-24](#2022-01-24) - **FIX Order Entry** - Update Third Party Execution Report details ### [2021-12-20](#2021-12-20) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `RNDR`, `MC`, `GALA`, `ENS`, `KP3R`, `CVC`, `ELON`, `MIM`, `SPELL` ### [2021-11-13](#2021-11-13) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `WCFG`, `RARE`, `RAD`, `QNT`, `NMR`, `MASK`, `FET`, `ASH`, `AUDIO`, `API3`, `USDC`, `SHIB` ### [2021-10-06](#2021-10-06) - **REST** - **New Feature** Documentation for [Wrap Order](/rest/orders#wrap-order) ### [2021-09-15](#2021-09-15) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `AXS`, `SLP`, `LUNA`, `UST`, `MCO2` ### [2021-08-06](#2021-08-06) - **REST** - **New Feature** Documentation for Earn Balances, Earn Rates and Earn Interest ### [2021-07-21](#2021-07-21) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `XTZ` ### [2021-07-14](#2021-07-14) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `CTX` ### [2021-06-16](#2021-06-16) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `ALCX`, `MIR`, `FTM`, `ANKR` ### [2021-06-07](#2021-06-07) - **REST** - **API Change** Added `is_clearing_fill` to [List Past Trades](/rest/orders#list-past-trades) response ### [2021-05-06](#2021-05-06) - **REST** - **New Feature** Documentation for SEN Withdrawals ### [2021-05-05](#2021-05-05) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `DOGE` ### [2021-04-27](#2021-04-27) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `CUBE`, `LPT`, `BOND`, `MATIC`, `INJ`, `SUSHI` ### [2021-04-16](#2021-04-16) - **REST** - **API Change** [List Approved Addresses](/rest/fund-management#list-approved-addresses) has changed from a `GET` to a `POST` HTTP request ### [2021-04-08](#2021-04-08) - **REST** - **API Change** [Symbol Details](/rest/market-data#get-symbol-details) endpoint has updated parameters ### [2021-03-29](#2021-03-29) - **Websocket** - Documentation for new [Multi Market Data Feed](/websocket/multi-market-data/index) ### [2021-03-22](#2021-03-22) - **REST** - **Websocket** - **FIX Market Data** - Documentation for new token support: `SKL`, `GRT`, `BNT`, `1INCH`, `ENJ`, `LRC`, `SAND` ### [2021-03-11](#2021-03-11) - **Websocket** - Added new optional [heartbeat filter](/websocket/order-events/about) ### [2021-02-23](#2021-02-23) - **REST** - **New Feature** Documentation for [Order Status](/rest/orders#get-order-status) now includes an optional `include_trades` parameter ### [2021-01-28](#2021-01-28) - **REST** - **Websocket** - Documentation for new symbol support: `BTCSGD` and `ETHSGD` ### [2020-10-28](#2020-10-28) - **REST** - **New Feature** Documentation for new [Symbol Details](/rest/market-data#get-symbol-details) endpoint ### [2020-10-26](#2020-10-26) - **REST** - Documentation for new fiat support: `GBP` and `EUR` ### [2020-10-14](#2020-10-14) - **Websocket** - Documentation for new token support: `FIL` ### [2020-10-07](#2020-10-07) - **Websocket** - Documentation for new token support: `AAVE` ### [2020-10-05](#2020-10-05) - **Websocket** - **FIX Market Data** - Documentation for new order book support: `BTCDAI` and `ETHDAI` ### [2020-09-24](#2020-09-24) - **REST** - **FIX Market Data** - Documentation for new token support: `MKR`, `ZRX`, `KNC`, `MANA`, `STORJ`, `SNX`, `CRV`, `BAL`, `UNI`, `REN`, `UMA`, `YFI` ### [2020-09-11](#2020-09-11) - **REST** - **FIX Market Data** - Documentation for new token support: `AMP`, `COMP`, `PAXG` ### [2020-08-28](#2020-08-28) - **REST** - **Websocket** - **FIX Market Data** - Removing `DAIBTC`and `DAIETH` trading pairs ### [2020-07-23](#2020-07-23) - **REST** - **New Feature** Documentation for [Adding a Bank](/rest/fund-management#add-bank), [Viewing Payment Methods](/rest/fund-management#list-payment-methods) and [Account Detail](/rest/account-administration#get-account-detail) ### [2020-04-09](#2020-04-09) - **REST** - **Websocket** - Documentation for new token support: `BAT`, `DAI`, `LINK`, `OXT` ### [2020-03-05](#2020-03-05) - **REST** - **New Feature** Documentation for [Retrieving deposit addresses](/rest/fund-management#list-deposit-addresses), [List Prices](/rest/market-data#list-prices), and [Notional Balances](/rest/fund-management#get-notional-balances) ### [2019-11-22](#2019-11-22) - **Websocket** - Updated response messages for [Candles Data Feed](/websocket/market-data-v2/candles) on [Market Data v2](/websocket/market-data-v2/index) ### [2019-11-18](#2019-11-18) - **REST** - **New Feature** Updating Stop order documentation ### [2019-11-12](#2019-11-12) - **REST** - **New Feature** Documentation for Stop Orders and Group->Master API key reference changes - **FIX Order Entry** - Add documentation for Stop Order flow ### [2019-08-16](#2019-08-16) - **REST** - **New Feature** Documentation for [Account Administration APIs](/account-admin-endpoints) and Group Level API keys ### [2019-08-08](#2019-08-08) - **REST** - **New Feature** Documentation for [Gemini Clearing](/gemini-clearing) functionality - **FIX Order Entry** - Add documentation for Gemini Clearing - **FIX Drop Copy** - **New Feature:** Document [Third Party Support](/fix/drop-copy/party-ids-&-roles/third-party-support) ### [2019-06-20](#2019-06-20) - **Websocket** - Adding documentation for [Market Data v2](/websocket/market-data-v2/index) and [Candles Data Feed](/websocket/market-data-v2/candles) ### [2019-06-07](#2019-06-07) - **REST** - Add documentation for [ticker v2](/rest/market-data#get-ticker-v2) and [candles](/rest/market-data#list-candles) endpoints ### [2019-05-31](#2019-05-31) - **REST** - Add legacy parameter for LTC [address generation](/rest/fund-management#create-new-deposit-address) ### [2019-03-22](#2019-03-22) - **REST** - Add Gemini Dollar section and detail changes to [Withdrawals](/rest/fund-management#withdraw-crypto-funds) ### [2019-03-01](#2019-03-01) - **REST** - Document changes to [Notional Volume](/rest/orders#get-notional-trading-volume) ### [2018-09-21](#2018-09-21) - **FIX Drop Copy** - Updated OMS third party trade capture report example ### [2018-09-14](#2018-09-14) - **REST** - Document new `fill-or-kill` order placement options - **FIX Order Entry** - Add Documentation for fill-or-kill orders ### [2018-09-10](#2018-09-10) - **REST** - Added Gemini dollar example to [Transfers](/rest/fund-management#list-past-transfers) ### [2018-08-07](#2018-08-07) - **FIX Market Data** - **API Change** Added support for maker side using custom tags 9002 `MDEntryMakerSide` and `EnableMDEntryMakerSide` ### [2018-06-18](#2018-06-18) - **FIX Order Entry** - Add [examples](#server-side-cancellations) for [order cancel reason](#order-cancel-reasons) ### [2018-06-06](#2018-06-06) - **Websocket** - **API Change** Market depth and entry filtering added to [Market Data](/websocket/market-data/index) API ### [2018-05-18](#2018-05-18) - **FIX Order Entry** - **FIX Market Data** - **FIX Drop Copy** - Add additional examples of using the API ### [2018-04-30](#2018-04-30) - **FIX Order Entry** - Document actual examples of Reject messages and Execution Report rejects in an [errors](#errors) section ### [2018-04-06](#2018-04-06) - **REST** - **New Feature:** Document [Transfers](/rest/fund-management#list-past-transfers) endpoint - **New Feature:** Document block trading support - **Websocket** - **API Change** Document block trades in [Market Data](/websocket/market-data/index) and [Order Events](/websocket/order-events/about) APIs - **FIX Order Entry** - **New Feature:** Document block trading support. Add [IOI \<6\>](#ioi-lt-6-gt) for broadcast of IOI to block trade market makers and update [New Order Single \](#new-order-single-lt-d-gt-limit) with options for placing an IOI and responding to an IOI - **FIX Market Data** - **FIX Drop Copy** - **New Feature:** Document block trading support ### [2018-03-09](#2018-03-09) - **FIX Market Data** - _Documentation bugfix:_ correct [MsgType \<35\>][MsgType] tag number in documentation for [Symbol List Request \](#symbol-list-request-lt-x-gt) - _Documentation improvement:_ added examples of [Symbol List Request \](#symbol-list-request-lt-x-gt) and [Symbol List \](#symbol-list-lt-y-gt) messages ### [2018-02-22](#2018-02-22) - **REST** - Document [List Past Trades](/rest/orders#list-past-trades) endpoint recipe for retrieving full trade history - **FIX Order Entry** - **New Feature:** Document [Third Party Support](#third-party-support). Add [OnBehalfOfCompID \<115\>][OnBehalfOfCompID] field to [Standard Header](#standard-header) - **FIX Drop Copy** - **New Feature:** Document [Third Party Support](/fix/drop-copy/party-ids-&-roles/third-party-support). Clarify usage of [Party IDs and Roles](#party-ids-and-roles) ### [2018-02-09](#2018-02-09) - **REST** - **API Change** update API [Error Codes](/error-codes) ### [2018-02-08](#2018-02-08) - **REST** - Better explanation of how Gemini rate limits public and private API requests; better client order id documentation - **Websocket** - Better market data examples ### [2018-01-22](#2018-01-22) - **FIX Market Data** - _Documentation bugfix:_ fixed [ExpireTime \<126\>][ExpireTime] tag number in documentation for [Market Data - Incremental Refresh \](#market-data-incremental-refresh-lt-x-gt) and [Market Data - Snapshot / Full Refresh \](#market-data-snapshot-full-refresh-lt-w-gt) - _Documentation improvement:_ added examples of [Market Data Request \](#market-data-request-lt-v-gt), [Market Data - Incremental Refresh \](#market-data-incremental-refresh-lt-x-gt), and [Market Data - Snapshot / Full Refresh \](#market-data-snapshot-full-refresh-lt-w-gt) messages ### [2017-12-01](#2017-12-01) - **REST** - **API Change** added `collar_price` to Current Auction and Auction History - **Websocket** - **API Change** `collar_price` added to [Market Data](/websocket/market-data/index) API - **FIX Market Data** - **API Change** added [MDEntryType \<269\>][MDEntryType] value `3 = INDEX_VALUE` to [Market Data - Incremental Refresh \](#market-data-incremental-refresh-lt-x-gt) to support reporting the auction collar price ### [2017-11-30](#2017-11-30) - **REST** - **API Change** clarify that only seven calendar days of data will be available through public API endpoints at [Trade History](/rest/market-data#list-trades) and Auction History. Email [support@gemini.com](mailto:support@gemini.com) for information about Gemini market data ### [2017-11-17](#2017-11-17) - **FIX Order Entry** - Add ExecInst=6 for Maker-or-Cancel orders ### [2017-08-30](#2017-08-30) - **FIX Market Data** - _Documentation bugfix:_ clarified description of [MDEntryType \<269\>][MDEntryType] in [Market Data - Snapshot / Full Refresh \](#market-data-snapshot-full-refresh-lt-w-gt) ### [2017-08-10](#2017-08-10) - **Websocket** - **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](/websocket/market-data/index) and [Order Events](/websocket/order-events/about) APIs. Further details available in [Sequence Numbers](/websocket/overview/socket-sequence) ### [2017-07-27](#2017-07-27) - **REST** - Improved code sampled in [Private API Invocation](/authentication/api-key#private-api-invocation) - Added [Troubleshooting](/troubleshooting#troubleshooting) section - Clarified precision in [Symbols and Minimums](/market-data/symbols-and-minimums) - _Documentation bugfix:_ [List Past Trades](/rest/orders#list-past-trades) endpoint now clarifies that `timestamp` parameter is on or after, not just after - **Websocket** - _Documentation bugfix:_ clarify the purpose of the `trace_id` in [Order Events: Heartbeats](/websocket/order-events/event-types#heartbeats) ### [2017-07-13](#2017-07-13) - **Websocket** - **API Change** `timestamp` and `timestampms` added to [Market Data](/websocket/market- ### [2017-05-31](#2017-05-31) - **REST** - Added explanation about using limit orders with the `immediate-or-cancel` execution option instead of market orders to [Create New Order](/rest/orders#create-new-order) endpoint ### [2017-05-22](#2017-05-22) - **REST** - **API Change** bugfix to `timestamp` handing in `POST` requests. Previously a timestamp submitted as a string was silently ignored; timestamps submitted as strings will now be parsed. No `timestamp` request parameter will be silently discarded. Updated documentation at [Data Types: Timestamps](/rest/~schemas#timestamp-type) to reflect timestamp behavior in requests and responses ### [2017-05-19](#2017-05-19) - **REST** - **API Change** order status JSON changed to always include an `options` array with [order execution options](/rest/orders#create-new-order). If no order execution options were submitted with the original order, the array will be empty ### [2017-05-15](#2017-05-15) - **REST** - Better JSON examples for all the [Order Status API](/rest/orders#get-order-status) endpoints - **Websocket** - Clarified how Gemini rate limits incoming requests to public WebSocket APIs - Document Order Events [Subscription Acknowledgement](/websocket/order-events/event-types#subscription-acknowledgement) `subscriptionId` field ### [2017-05-02](#2017-05-02) - **REST** - Clarify how rate limits are applied ### [2017-03-30](#2017-03-30) - **REST** - Improved documentation for [List Past Trades](/rest/orders#list-past-trades) and [Get Available Balances](/rest/fund-management#get-available-balances) ### [2017-03-06](#2017-03-06) - **Websocket** - Documentation bugfix: correct location of market data JSON example for trade events ### [2017-02-22](#2017-02-22) - **REST** - Added recipe for retrieving full trade history from [List Past Trades](/rest/orders#list-past-trades) endpoint ### [2016-12-14](#2016-12-14) - **REST** - **Websocket** - New feature: API key roles, crypto deposit and withdrawal endpoints ### [2016-11-10](#2016-11-10) - **Websocket** - Initial WebSocket API documentation ### [2016-08-23](#2016-08-23) - **REST** - New feature: auction documentation added to new order placement, public APIs, and streaming market data ### [2016-05-31](#2016-05-31) - **REST** - Add ETH to supported symbols ### [2016-04-27](#2016-04-27) - **REST** - Document `marker-or-cancel` and `immediate-or-cancel` order placement options ### [2016-03-23](#2016-03-23) - **FIX Order Entry** - **FIX Market Data** - **FIX Drop Copy** - Initial FIX API documentation ### [2015-12-22](#2015-12-22) - **REST** - Document sandbox usage ### [2015-10-05](#2015-10-05) - **REST** - Initial REST API documentation --- URL: https://developer.gemini.com/build/agent.md # Build an agent Build an agent on Gemini's current APIs by combining canonical documentation, explicit account context, the minimum required permissions, and confirmation before side effects. > **Tooling status:** Gemini's open-source MCP server and packaged agent skills are available now. SDK packages and a dedicated Gemini API CLI are still in development. ## Start with current interfaces Use the [API Reference](/api-reference) to select a documented REST, WebSocket, or FIX capability. Agents and other LLM clients can discover the published documentation through [llms.txt](https://developer.gemini.com/llms.txt), or use the current [MCP server and agent skills](/tools). Choose authentication only after choosing the operation: - [API key authentication](/authentication/api-key) - [OAuth](/authentication/oauth) - [Roles and permissions](/roles) Credential support and account selection vary by interface. Do not assume that credential identity alone selects a trading account. ## Resolve intent before acting For each requested operation, an agent should determine: 1. The trading product and capability the user intends. 2. Whether the operation is read-only or has side effects. 3. Which credential type, role, or OAuth scope the operation requires. 4. Which account the operation will use according to the interface's documented behavior. 5. Whether that account can use the product and has the required balance or collateral. 6. Whether the operation supports a documented preview or validation step. If more than one account could satisfy the request, ask the user to choose. Do not create an undocumented default-selection rule. ## Guard mutating operations Before placing or cancelling an order, transferring funds, or making another state-changing request: - Restate the product, instrument, side effect, and resolved account. - Show material order or transfer parameters without exposing secrets. - Use a documented preview when one exists. - Ask for confirmation at the point of execution. - Record the returned identifier and monitor the documented status or event stream. - Stop on ambiguous account context, missing permission, unsupported product access, or an undocumented operation. Keep credentials out of prompts, logs, generated source, and tool output. Give the agent only the role or scope required for its workflow. ## Isolate and test Use [accounts and subaccounts](/rest-api/common/admin/subaccounts) when the documented account model fits the required isolation boundary. Use the [demo environment](/get-started/sandbox) only for products and operations explicitly documented there, and test both success and refusal paths before using production credentials. ## Agent tooling [SDKs & Tools](/tools) documents the available Gemini API MCP server, API samples, and agent skills. SDK packages and a dedicated Gemini API CLI will be added as they are released. Treat documentation retrieval and trading execution as distinct capabilities, and verify availability, permissions, account selection, and confirmation behavior before invoking a mutating tool. --- URL: https://developer.gemini.com/authentication/oauth.md # OAuth 2.0 Gemini supports [OAuth 2.0](https://oauth.net/2/) for user-authorized API access. Gemini implements the authorization code grant flow with refresh tokens (`response_type=code`). To get started, create an OAuth application in [API Settings](https://exchange.gemini.com/settings/api). Enter your application name, description, logo, and requested [scopes](/authentication/oauth#oauth-scopes). When you create an app you choose its client type, and that choice is permanent: - A **confidential client** receives a `client_id` and a `client_secret`. Use this for applications running on secure servers you control. - A **public client** receives a `client_id` only. Use this for single-page apps, mobile apps, and desktop clients. Public clients must use [PKCE](#public-clients-and-pkce). Your `client_id` identifies your app in every request. Confidential clients also send a `client_secret` in token POST requests. Public clients never send secrets and use PKCE instead. You cannot change a client type after creation. Gemini reviews your registered application before activating it in production. You can register applications immediately in the [Sandbox Environment](https://exchange.sandbox.gemini.com/settings/api) for testing. Email trading@gemini.com with questions. OAuth 2.0 uses short-lived access tokens (24-hour expiration) for API requests and non-expiring refresh tokens to generate new access tokens. ## Authorization Code Grant Flow In the authorization code flow, you redirect users to Gemini to grant permissions. Gemini returns an authorization code that your backend exchanges for access and refresh tokens. ### Authorization Request Redirect users to Gemini to authorize access to your application. Users log in and approve requested scopes. `GET https://exchange.gemini.com/auth` :::note Example authorization request (confidential client): ::: `GET https://exchange.gemini.com/auth?client_id=my_id&response_type=code&redirect_uri=www.example.com/redirect&state=82350325&scope=balances:read,orders:create` :::note Example authorization request (public client — adds the PKCE `code_challenge` and `code_challenge_method`): ::: `GET https://exchange.gemini.com/auth?client_id=my_id&response_type=code&redirect_uri=http://127.0.0.1:51234/callback&state=82350325&scope=balances:read,orders:create&code_challenge=5S_YsMh19iBDX5plIVTXdtF3iJCbJ388EEVd5CVlWxU&code_challenge_method=S256` ### URL Parameters
Parameter Type Description
client_id string Unique id of your application. This is provided in your [API settings](https://exchange.gemini.com/settings/api)
response_type string The literal string "code"
redirect_uri string The URL users should be returned to when they authorize. Note, this URL must be included in your list of approved redirect_uris in your app registration
state string A random string that will be returned to you in the response. Required (and must be non-empty) for public clients to protect against CSRF; strongly recommended for confidential clients.
scope string A comma separated list of [scopes](#oauth-scopes) corresponding to the access you're requesting for your application. Note, these scopes must be included in your list of scopes in your app registration
code_challenge string Required for public clients. The PKCE code challenge: `BASE64URL-no-padding(SHA-256(code_verifier))`. Always 43 characters for the `S256` method. See [Public Clients and PKCE](#public-clients-and-pkce)
code_challenge_method string Required for public clients. The literal string "S256". `plain` is not accepted
## Authorization Response :::note Example `redirect_uri` response after user login ::: `https://www.example.com/redirect?code=90123465-86ee-44ef-b4e3-835cc89bc8a3&state=82350325` On successful authorization Gemini will redirect your user to the `redirect_uri` you supplied with additional parameters `code` and `state`. The parameter `state` should match the `state` you provided, otherwise you should not trust the response. `code` is a temporary code which you will then use to obtain access and refresh tokens. ### Authorization Token Request Once you have received a `code` you can exchange it for access and refresh tokens. `POST https://exchange.gemini.com/auth/token`
Parameter Type Description
client_id string Unique id of your application. This is provided in your [API settings](https://exchange.gemini.com/settings/api)
client_secret string Secret of your application, provided when you register a confidential client in [API settings](https://exchange.gemini.com/settings/api). Confidential clients only — public clients must not send this, and a request that includes it will fail
code string The code you received from the authorization request
redirect_uri string This must match the `redirect_uri` provided in the authorization request
grant_type string The literal string "authorization_code"
code_verifier string Required for public clients. The original `code_verifier` you generated before the authorization request (43–128 characters from `[A-Za-z0-9-._~]`). Gemini hashes it and compares it to the `code_challenge` you sent
:::note Example token request (confidential client — sends `client_secret`): ::: ```json { "client_id": "my_id", "client_secret": "my_secret", "code": "90123465-86ee-44ef-b4e3-835cc89bc8a3", "redirect_uri": "www.example.com/redirect", "grant_type": "authorization_code" } ``` :::note Example token request (public client — sends `code_verifier`, no `client_secret`): ::: ```json { "client_id": "my_id", "code": "90123465-86ee-44ef-b4e3-835cc89bc8a3", "redirect_uri": "http://127.0.0.1:51234/callback", "grant_type": "authorization_code", "code_verifier": "M25iVXpKU3puUjFaYWg3T1NDTDQtcW1ROUY5YXlwalNoc0hhakx-fkdq" } ``` ### Authorization Token Response
Field Type Description
access_token string A short-lived token to use in API call authentication. Is valid until the `expires_in` time reaches 0
refresh_token string A refresh token to be used to generate new access tokens
token_type string The literal string "bearer"
scope string The [scopes](#oauth-scopes) the access token will have access to
expires_in integer The lifetime in seconds of the access token, as measured in seconds from the current time
:::note Example Token Response ::: ```json { "access_token": "d9af2411-3e85-41bb-89f4-cf53750f04df", "refresh_token": "215c5a89-6df7-457b-ba0b-70695da8c91f", "token_type": "bearer", "scope": "balances:read,orders:create", "expires_in": 86399 } ``` ## Public Clients and PKCE A public client is an OAuth app that runs somewhere it cannot keep a secret — a native mobile app, a desktop app, or a single-page app. Because the code ships to the user's device or browser, anyone can read it, so there is no `client_secret` to protect the token exchange. Public clients close that gap with PKCE (Proof Key for Code Exchange, [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636)). PKCE is required for every public client, and it only works with the authorization code flow. Your app generates a one-time secret before it starts, sends only a hash of that secret when it asks for an authorization code, and reveals the original secret when it exchanges the code for tokens. Gemini hashes what you reveal and checks it against the hash you sent earlier. An attacker who intercepts the authorization code cannot use it, because they never saw the original secret. ### How PKCE works 1. **Generate a `code_verifier`.** A high-entropy random string of 43–128 characters using only `A-Z`, `a-z`, `0-9`, `-`, `.`, `_`, `~` ([RFC 7636 §4.1](https://datatracker.ietf.org/doc/html/rfc7636#section-4.1)). 2. **Derive the `code_challenge`.** `BASE64URL-no-padding(SHA-256(code_verifier))` — exactly 43 characters for `S256`. 3. **Send the challenge on the authorization request.** On `GET https://exchange.gemini.com/auth`, include `code_challenge`, `code_challenge_method=S256`, and a non-empty `state`. `S256` is the only method Gemini accepts; `plain` is rejected. 4. **Send the verifier on the token request.** On `POST https://exchange.gemini.com/auth/token`, include the original `code_verifier`. Do **not** include a `client_secret`. Gemini hashes the verifier and compares it to the challenge from step 3. Public clients must also send a non-empty `state` on the authorization request. For confidential clients `state` is strongly recommended; for public clients it is required, because there is no secret to anchor the request and `state` is your protection against CSRF. Compare the `state` in the response to the value you sent, and reject the response if they don't match. Refresh works the same way for a public client as for a confidential one, with one difference: omit the `client_secret`. There is no `code_verifier` on a refresh request. **Redirect URIs for public clients.** To support native and desktop apps ([RFC 8252](https://datatracker.ietf.org/doc/html/rfc8252)), a public client may register an `http` loopback redirect URI — `http://localhost`, `http://127.0.0.1`, or `http://[::1]` — and the port may vary at runtime, so an ephemeral port chosen by the OS will be accepted. Loopback redirect URIs must use `http` (not `https`) and must not include user info. Every other (non-loopback) redirect URI must match your registered URI exactly. :::note Generating a `code_verifier` and deriving the `S256` `code_challenge` in Python: ::: ```python import hashlib import base64 import secrets # 1. High-entropy code_verifier (token_urlsafe uses the unreserved set; 64 bytes -> ~86 chars) code_verifier = secrets.token_urlsafe(64) # 2. code_challenge = BASE64URL-no-padding( SHA-256( code_verifier ) ) digest = hashlib.sha256(code_verifier.encode("ascii")).digest() code_challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode("ascii") # Send code_challenge + code_challenge_method=S256 on the authorization request, # then send code_verifier on the token request. ``` ## Using Refresh Tokens The access token you receive will be relatively short-lived (default 24 hours). Once an access token has expired you can use your refresh token to generate a new access token. Refresh tokens never expire, however they are one-time use only as your request for a new access token will also return a new refresh token. Getting a new access token is similar to getting the initial access and refresh tokens with slightly different parameters ### Refresh Token Request `POST https://exchange.gemini.com/auth/token` :::note Example Token Request ::: ```json { "client_id": "my_id", "client_secret": "my_secret", "refresh_token": "215c5a89-6df7-457b-ba0b-70695da8c91f", "grant_type":"refresh_token" } ```
Parameter Type Description
client_id string Unique id of your application
client_secret string Secret of your application. This is provided when you first register an app in [API settings](https://exchange.gemini.com/settings/api)
refresh_token string Your refresh token
grant_type string The literal string "refresh_token"
### Refresh Token Response The response is the same as the initial token response – it will contain a new access token to query APIs and a new refresh token for when the access token expires.
Parameter Type Description
access_token string A short-lived token to use in API call authentication
refresh_token string A refresh token to be used to generate new access tokens
token_type string The literal string "bearer"
scope string The [scopes](#oauth-scopes) the access token will have access to
expires_in integer The lifetime in seconds of the access token, as measured in seconds from the current time
:::note Example Token Response ::: ```json { "access_token": "c5e9459d-dc6f-4567-bce4-050ec965f22e", "expires_in": 86399, "scope": "balances:read,orders:create", "refresh_token": "ce0f14af-74dd-4767-a4e7-286e98b944c1", "token_type": "bearer" } ``` ## Using Access Tokens Once you have an access token you can use it to call any Gemini API. Most of the examples for the private APIs in these docs make use of API keys and [corresponding headers](/authentication/api-key#private-api-invocation), to use an access token simply update your header: | Header | Description | |------------------|--------------------------------------------------------------------------------| | Authorization | The literal string "Bearer " concatenated to your temporary `access_token` | | X-GEMINI-PAYLOAD | The base64-encoded JSON payload (the payloads on OAuth do not require a nonce) | ```python import requests import json import base64 url = "https://api.gemini.com/v1/mytrades" access_token = "Bearer d9af2411-3e85-41bb-89f4-cf53750f04df" payload = { "request": "/v1/mytrades", "symbol": "btcusd" } encoded_payload = json.dumps(payload).encode() b64 = base64.b64encode(encoded_payload) request_headers = { "Authorization": access_token, "X-GEMINI-PAYLOAD": b64 } response = requests.post(url, data=None, headers=request_headers, verify=False) my_trades = response.json() ``` ```python import requests import json import base64 url = "https://api.gemini.com/v1/orders/history" access_token = "Bearer d9af2411-3e85-41bb-89f4-cf53750f04df" payload = { "request": "/v1/orders/history", "symbol": "btcusd" } encoded_payload = json.dumps(payload).encode() b64 = base64.b64encode(encoded_payload) request_headers = { "Authorization": access_token, "X-GEMINI-PAYLOAD": b64 } response = requests.post(url, data=None, headers=request_headers, verify=False) my_orders = response.json() ``` ## OAuth Scopes Gemini uses a role-based system for its API. All OAuth applications are limited to the scopes in the following chart:
Endpoint URI Scope
[Get Deposit Addresses](/rest/fund-management#list-deposit-addresses) `/v1/addresses/:network` `addresses:read, addresses:create`
[New Deposit Address](/rest/fund-management#create-new-deposit-address) `/v1/deposit/:network/newAddress` `addresses:create`
[List Approved Addresses](/rest/fund-management#list-approved-addresses) `/v1/approvedAddresses/account/:network` `addresses:read`
[Remove Approved Address](/rest/fund-management#remove-approved-address) `/v1/approvedAddresses/:network/remove` `addresses:create`
[Get Available Balances](/rest/fund-management#get-available-balances) `/v1/balances` `balances:read`
[Get Notional Balances](/rest/fund-management#get-notional-balances) `v1/notionalbalances/:currency` `balances:read`
[Add A Bank](/rest/fund-management#add-bank) `/v1/payments/addbank` `banks:create`
[Add A Bank CAD](/rest/fund-management#add-bank-cad) `/v1/payments/addbank/cad` `banks:create`
[View Payment Methods](/rest/fund-management#list-payment-methods) `/v1/payments/methods` `banks:read, banks:create`
[New Clearing Order](/rest/clearing#create-new-clearing-order) `/v1/clearing/new` `clearing:create`
[Cancel Clearing Order](/rest/clearing#cancel-clearing-order) `/v1/clearing/cancel` `clearing:create`
[Confirm Clearing Order](/rest/clearing#confirm-clearing-order) `/v1/clearing/confirm` `clearing:create`
[Clearing Order Status](/rest/clearing#get-clearing-order) `/v1/clearing/status` `clearing:read`
[Clearing Order List](/rest/clearing#list-clearing-orders) `/v1/clearing/list` `clearing:read`
[Clearing Broker List](/rest/clearing#list-clearing-brokers) `/v1/clearing/broker/list` `clearing:read`
[Clearing Trades](/rest/clearing#list-clearing-trades) `/v1/clearing/trades` `clearing:read`
[Withdraw Crypto Funds](/rest/fund-management#withdraw-crypto-funds) `/v2/withdraw/:network/:ticker` `crypto:send`
[List Past Trades](/rest/orders#list-past-trades) `/v1/mytrades` `history:read`
[Get Orders History](/rest/orders#list-past-orders) `/v1/orders/history` `history:read`
[Get Notional Volume](/rest/orders#get-notional-trading-volume) `/v1/notionalvolume` `history:read`
[Get Trade Volume](/rest/orders#get-trading-volume) `/v1/tradevolume` `history:read`
[Transfers](/rest/fund-management#list-past-transfers) `/v2/transfers` `history:read`
[Custody Account Fees](/rest/fund-management#list-custody-fee-transfers) `/v1/custodyaccountfees` `history:read`
[Create New Order](/rest/orders#create-new-order) `/v1/order/new` `orders:create`
[Cancel Order](/rest/orders#cancel-order) `/v1/order/cancel` `orders:create`
[Cancel All Session Orders](/rest/orders#cancel-all-session-orders) `/v1/order/cancel/session` `orders:create`
[Cancel All Active Orders](/rest/orders#cancel-all-active-orders) `/v1/order/cancel/all` `orders:create`
[Wrap Order](/rest/orders#wrap-order) `/v1/wrap/:symbol` `orders:create`
[Get Instant Quote](/rest/instant#get-instant-quote) `/v1/instant/quote` `orders:create`
[Execute Instant Order](/rest/instant#execute-instant-order) `/v1/instant/execute` `orders:create`
[Get Order Status](/rest/orders#get-order-status) `/v1/order/status` `orders:read`
[Get Active Orders](/rest/orders#list-active-orders) `/v1/orders` `orders:read`
[Account Detail](/rest/account-administration#get-account-detail) `/v1/account` `account:read`
[Get Terms Status](/rest-api/prediction-markets/terms/get-terms-status) `/v1/prediction-markets/terms/status` `orders:read`
[Accept Terms](/rest-api/prediction-markets/terms/accept-terms) `/v1/prediction-markets/terms/accept` `orders:create`
[Place Prediction Market Order](/rest-api/prediction-markets/order-management/place-order) `/v1/prediction-markets/order` `orders:create`
[Place Prediction Market Batch Orders](/rest-api/prediction-markets/order-management/place-batch-orders) `/v1/prediction-markets/order/batch` `orders:create`
[Cancel Prediction Market Order](/rest-api/prediction-markets/order-management/cancel-order) `/v1/prediction-markets/order/cancel` `orders:create`
[Cancel Prediction Market Batch Orders](/rest-api/prediction-markets/order-management/cancel-batch-orders) `/v1/prediction-markets/order/batch/cancel` `orders:create`
[Get Active Prediction Market Orders](/rest-api/prediction-markets/order-management/get-active-orders) `/v1/prediction-markets/orders/active` `orders:read`
[Get Prediction Market Order History](/rest-api/prediction-markets/order-management/get-order-history) `/v1/prediction-markets/orders/history` `orders:read`
[Get Prediction Market Positions](/rest-api/prediction-markets/positions/get-positions) `/v1/prediction-markets/positions` `orders:read`
[Prediction Market WebSocket Position Updates](/prediction-markets/websocket/streams#position-updates) `positions@account`, `positions@account@1s` `positions:read` or `predictions:positions:read`
[Get Settled Prediction Market Positions](/rest-api/prediction-markets/positions/get-settled-positions) `/v1/prediction-markets/positions/settled` `orders:read`
[Get Prediction Market Volume Metrics](/rest-api/prediction-markets/positions/get-volume-metrics) `/v1/prediction-markets/metrics/volume` `orders:read`
[List Maker Rebate Payouts](/rest-api/prediction-markets/rewards/list-maker-rebate-payouts) `/v1/prediction-markets/maker-rebate/payouts` `orders:read`
[Get Maker Rebate Lifetime Summary](/rest-api/prediction-markets/rewards/get-maker-rebate-lifetime-summary) `/v1/prediction-markets/maker-rebate/summary/total` `orders:read`
[Get Liquidity Rewards Daily Summary](/rest-api/prediction-markets/rewards/get-liquidity-rewards-daily-summary) `/v1/prediction-markets/liquidity-rewards/summary/daily` `orders:read`
[Get Liquidity Rewards Lifetime Summary](/rest-api/prediction-markets/rewards/get-liquidity-rewards-lifetime-summary) `/v1/prediction-markets/liquidity-rewards/summary/total` `orders:read`
API keys use different roles for to access Gemini APIs. Please see [roles](/roles) for descriptions of each role and scope for API keys. ## Revoke OAuth Token Clients can programmatically revoke an active access token or refresh token using the OAuth 2.0 Token Revocation endpoint ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)). `POST https://exchange.gemini.com/auth/token/revoke` ### Request Parameters | Parameter | Type | Description | |---|---|---| | `token` | string | **Required.** The access token or refresh token to revoke. | | `client_id` | string | **Required.** Unique client ID of your application. | | `client_secret` | string | Secret of your application (Confidential clients only). | :::note Example Token Revocation Request (Confidential Client): ::: ```json { "client_id": "my_id", "client_secret": "my_secret", "token": "215c5a89-6df7-457b-ba0b-70695da8c91f" } ``` ### Response On success, Gemini returns HTTP `200 OK` with an empty response body. In accordance with RFC 7009 §2.2, HTTP `200 OK` is returned regardless of whether the token was currently active or previously revoked. --- ## Token Introspection & Authorization Metadata Gemini also provides standard OAuth 2.0 endpoints for token introspection and server metadata discovery: - **Token Introspection** ([RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662)): `POST https://exchange.gemini.com/auth/token/introspect` Pass `client_id` and `token` to inspect whether a token is active, its scopes, and its expiration timestamp. - **Authorization Server Metadata** ([RFC 8414](https://datatracker.ietf.org/doc/html/rfc8414)): `GET https://exchange.gemini.com/.well-known/oauth-authorization-server` Returns supported grant types (`authorization_code`, `refresh_token`), response types (`code`), PKCE challenge methods (`S256`), and endpoint URIs. --- ## Revision History | Date | Notes | |---|---| | 2020/08/20 | Initial OAuth documentation | | 2026/08/03 | Updated OAuth 2.0 verification and added PKCE, token revocation, introspection, and server metadata specifications | --- URL: https://developer.gemini.com/authentication/api-key.md # API Key Authentication Gemini uses API key pairs to authenticate access to private REST APIs. You can provision API key pairs by logging into the exchange and navigating to [Settings/API](https://exchange.gemini.com/settings/api). When you create an API key, you will receive: - **API Key**: The public identifier for your API session (e.g., `account-...` or `master-...`). - **API Secret**: The private secret used to generate HMAC-SHA384 signatures for your request payloads. Never transmit or share your API secret. --- ## Request Anatomy & Headers Authenticated private REST API requests require sending an empty HTTP body (`Content-Length: 0`) and encoding the JSON request payload directly into the `X-GEMINI-PAYLOAD` header. > [!IMPORTANT] > Private REST endpoints do not submit JSON payloads in the HTTP POST body. Instead, the JSON object is Base64-encoded and passed in the `X-GEMINI-PAYLOAD` header, with an empty request body (`Content-Length: 0`). All private requests must include the following HTTP headers: | Header Name | Type | Value / Description | |---|---|---| | `Content-Length` | String | `0` | | `Content-Type` | String | `text/plain` | | `X-GEMINI-APIKEY` | String | Your Gemini API key identifier | | `X-GEMINI-PAYLOAD` | String | Base64-encoded JSON payload containing `request`, `nonce`, and endpoint parameters | | `X-GEMINI-SIGNATURE` | String | Hex-encoded HMAC-SHA384 signature of the Base64 payload: `hex(HMAC_SHA384(base64(payload), key=api_secret))` | | `Cache-Control` | String | `no-cache` | --- ## Nonce Management & Replay Protection Every authenticated request payload must contain a `"nonce"` field. The nonce guarantees request freshness and prevents replay attacks where an attacker captures and attempts to re-execute a signed request. When provisioning an API key, you can select one of two nonce validation modes: 1. **Time-Based Nonce** (Recommended): - Nonces must be Unix epoch timestamps in **seconds** (e.g., `1776294447`). - The server validates that the nonce timestamp is within `+/- 30` seconds of server time. - Ideal for distributed or stateless trading clients. 2. **Incremental Nonce**: - Nonces must be monotonically increasing numbers (e.g., Unix timestamp in milliseconds or a sequential integer). - Each request on a given session key must present a higher nonce than the previous request. - Nonces must increase strictly with respect to the specific API session key being used. --- ## Sessions & Heartbeat (Cancel on Disconnect) An account may have multiple active API keys provisioned concurrently. Each key represents an independent **session**. - **Session Isolation**: Nonces are evaluated independently per session key, enabling multi-threaded or distributed systems to execute orders concurrently without cross-thread clock synchronization. - **Session Operations**: Certain API actions (such as [Cancel All Session Orders](/trading/rest-api/orders/cancel-all-session-orders)) act exclusively on open orders placed by that specific API key session. ### Require Heartbeat Option When creating a key, you can enable the **Requires Heartbeat** setting. - If no authenticated request or explicit [Heartbeat](/trading/rest-api/orders/heartbeat) message is received for **30 seconds**, the exchange automatically cancels all outstanding open orders for that session. - To maintain an active session during periods of low trading activity, send periodic [Heartbeat](/trading/rest-api/orders/heartbeat) requests at a recommended interval of **15 seconds**. Any valid authenticated request automatically resets the 30-second timer. --- ## Subaccount Operations (Master API Keys) Accounts organized within an Account Group can provision **Master API Keys** to manage multiple subaccounts from a single credential. - **Key Formats**: - Master API Keys are prefixed with `master-`. - Standard Account API Keys are prefixed with `account-`. - **Targeting Subaccounts**: - Include an `"account"` parameter in your request JSON payload containing the target subaccount nickname or short name (e.g., `"account": "primary"` or `"account": ["sub-1", "sub-2"]`). - For full details on account group hierarchy and subaccount patterns, see [Subaccounts](/rest-api/common/admin/subaccounts). --- ## Authentication Error Codes If an authenticated request fails signature verification or header validation, the API returns a `400 Bad Request` or `403 Forbidden` response: | Error Code | HTTP Status | Cause / Resolution | |---|---|---| | `MissingApikeyHeader` | `400` | The `X-GEMINI-APIKEY` header was omitted. | | `MissingPayloadHeader` | `400` | The `X-GEMINI-PAYLOAD` header was omitted. | | `MissingSignatureHeader` | `400` | The `X-GEMINI-SIGNATURE` header was omitted. | | `InvalidNonce` | `400` | The nonce is outside the 30-second server time window or did not strictly increase. | | `InvalidSignature` | `400` | The HMAC-SHA384 signature did not match the computed hash of `X-GEMINI-PAYLOAD`. | | `AmbiguousAuthentication` | `400` | Both V1 API key headers (`X-GEMINI-APIKEY`) and OAuth/V2 headers were supplied in the same request. | | `InvalidApiKey` | `403` | The API key does not exist or has been disabled. | --- ## Request Signing Code Examples The following code examples demonstrate how to construct the JSON payload, Base64-encode it into `X-GEMINI-PAYLOAD`, calculate the `HMAC-SHA384` signature, and execute a private POST request against `/v1/order/status`. ```bash title="cURL" # Set credentials API_KEY="GEMINI_API_KEY" API_SECRET="GEMINI_API_SECRET" # Create JSON payload with timestamp nonce NONCE=$(date +%s) PAYLOAD=$(echo -n "{\"request\":\"/v1/order/status\",\"nonce\":$NONCE,\"order_id\":105575824}" | base64 | tr -d '\n') SIGNATURE=$(echo -n "$PAYLOAD" | openssl dgst -sha384 -hmac "$API_SECRET" | cut -d ' ' -f2) # Execute private POST request curl -X POST "https://api.gemini.com/v1/order/status" \ -H "Content-Type: text/plain" \ -H "Content-Length: 0" \ -H "X-GEMINI-APIKEY: $API_KEY" \ -H "X-GEMINI-PAYLOAD: $PAYLOAD" \ -H "X-GEMINI-SIGNATURE: $SIGNATURE" \ -H "Cache-Control: no-cache" ``` ```python title="Python" import json import base64 import hmac import hashlib import time import requests url = "https://api.gemini.com/v1/order/status" gemini_api_key = "GEMINI_API_KEY" gemini_api_secret = "GEMINI_API_SECRET".encode() payload = { "request": "/v1/order/status", "nonce": int(time.time()), "order_id": 105575824 } encoded_payload = json.dumps(payload).encode() b64_payload = base64.b64encode(encoded_payload) signature = hmac.new(gemini_api_secret, b64_payload, hashlib.sha384).hexdigest() headers = { "Content-Type": "text/plain", "Content-Length": "0", "X-GEMINI-APIKEY": gemini_api_key, "X-GEMINI-PAYLOAD": b64_payload.decode(), "X-GEMINI-SIGNATURE": signature, "Cache-Control": "no-cache" } response = requests.post(url, headers=headers) print(response.json()) ``` ```javascript title="Node.js" const crypto = require('crypto'); const axios = require('axios'); const url = "https://api.gemini.com/v1/order/status"; const apiKey = "GEMINI_API_KEY"; const apiSecret = Buffer.from("GEMINI_API_SECRET"); const payload = { request: "/v1/order/status", nonce: Math.floor(Date.now() / 1000), order_id: 105575824 }; const b64Payload = Buffer.from(JSON.stringify(payload)).toString('base64'); const signature = crypto.createHmac('sha384', apiSecret) .update(b64Payload) .digest('hex'); const headers = { 'Content-Type': 'text/plain', 'Content-Length': '0', 'X-GEMINI-APIKEY': apiKey, 'X-GEMINI-PAYLOAD': b64Payload, 'X-GEMINI-SIGNATURE': signature, 'Cache-Control': 'no-cache' }; axios.post(url, null, { headers }) .then(res => console.log(res.data)) .catch(err => console.error(err.response ? err.response.data : err)); ``` ```go title="Go" package main import ( "crypto/hmac" "crypto/sha512" "encoding/base64" "encoding/hex" "encoding/json" "fmt" "net/http" "time" ) func main() { endpoint := "https://api.gemini.com/v1/order/status" apiKey := "GEMINI_API_KEY" apiSecret := []byte("GEMINI_API_SECRET") payload := map[string]interface{}{ "request": "/v1/order/status", "nonce": time.Now().Unix(), "order_id": 105575824, } jsonBytes, _ := json.Marshal(payload) b64Payload := base64.StdEncoding.EncodeToString(jsonBytes) h := hmac.New(sha512.New384, apiSecret) h.Write([]byte(b64Payload)) signature := hex.EncodeToString(h.Sum(nil)) req, _ := http.NewRequest("POST", endpoint, nil) req.Header.Set("Content-Type", "text/plain") req.Header.Set("Content-Length", "0") req.Header.Set("X-GEMINI-APIKEY", apiKey) req.Header.Set("X-GEMINI-PAYLOAD", b64Payload) req.Header.Set("X-GEMINI-SIGNATURE", signature) req.Header.Set("Cache-Control", "no-cache") client := &http.Client{} resp, err := client.Do(req) if err != nil { fmt.Println("Error:", err) return } defer resp.Body.Close() fmt.Println("Status Code:", resp.StatusCode) } ``` ```java title="Java" import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.time.Instant; import java.util.Base64; import java.util.HashMap; import java.util.Map; import org.json.JSONObject; public class GeminiApiExample { public static void main(String[] args) throws Exception { String apiKey = "GEMINI_API_KEY"; String apiSecret = "GEMINI_API_SECRET"; JSONObject payload = new JSONObject(); payload.put("request", "/v1/order/status"); payload.put("nonce", Instant.now().getEpochSecond()); payload.put("order_id", 105575824); byte[] encodedPayload = payload.toString().getBytes(StandardCharsets.UTF_8); String b64Payload = Base64.getEncoder().encodeToString(encodedPayload); Mac sha384HMAC = Mac.getInstance("HmacSHA384"); SecretKeySpec secretKeySpec = new SecretKeySpec( apiSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA384"); sha384HMAC.init(secretKeySpec); byte[] signatureBytes = sha384HMAC.doFinal(b64Payload.getBytes(StandardCharsets.UTF_8)); StringBuilder signature = new StringBuilder(); for (byte b : signatureBytes) { signature.append(String.format("%02x", b)); } System.out.println("X-GEMINI-APIKEY: " + apiKey); System.out.println("X-GEMINI-PAYLOAD: " + b64Payload); System.out.println("X-GEMINI-SIGNATURE: " + signature.toString()); } } ``` ```rust title="Rust" use std::time::{SystemTime, UNIX_EPOCH}; use hmac::{Hmac, Mac}; use sha2::Sha384; use base64::encode; use serde_json::json; fn main() { let api_key = "GEMINI_API_KEY"; let api_secret = b"GEMINI_API_SECRET"; let nonce = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); let payload = json!({ "request": "/v1/order/status", "nonce": nonce, "order_id": 105575824 }); let b64_payload = encode(payload.to_string().as_bytes()); type HmacSha384 = Hmac; let mut mac = HmacSha384::new_from_slice(api_secret).unwrap(); mac.update(b64_payload.as_bytes()); let signature = mac.finalize().into_bytes().iter() .map(|b| format!("{:02x}", b)) .collect::(); println!("API Key: {}", api_key); println!("Payload: {}", b64_payload); println!("Signature: {}", signature); } ``` --- URL: https://developer.gemini.com/websocket/archived/v2.md # Market Data v2 (Archived) :::warning This documentation is archived for reference only. These APIs have been replaced by the [**new WebSocket API**](/websocket/introduction). **Start new integrations with the new WebSocket API.** ::: # Market Data v2 The initial response will show the existing state of the order books and last 50 trades. Subsequent messages show all executed trades and order book changes. ## WebSocket Request `wss://api.gemini.com/v2/marketdata` After connecting, subscribe by sending a message: | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `subscribe` | | `subscriptions` | array | | | -- `name` | string | `l2`, `candles_1m`, `candles_5m`, `candles_15m`, `candles_30m`, `candles_1h`, `candles_6h`, `candles_1d` | | -- `symbols` | array | `["BTCUSD", "ETHBTC", ...]` | ### Subscription Example ```json { "type": "subscribe", "subscriptions": [ { "name": "l2", "symbols": ["BTCUSD", "ETHUSD"] } ] } ``` ```python import ssl, websocket, _thread as thread def on_message(ws, message): print(message) def on_open(ws): def run(*args): ws.send('{"type": "subscribe","subscriptions":[{"name":"l2","symbols":["BTCUSD","ETHUSD"]}]}') thread.start_new_thread(run, ()) ws = websocket.WebSocketApp("wss://api.gemini.com/v2/marketdata", on_message=on_message, on_open=on_open) ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE}) ``` ## Unsubscribe ```json { "type": "unsubscribe", "subscriptions": [ { "name": "l2", "symbols": ["BTCUSD", "ETHBTC"] } ] } ``` --- ## Level 2 Data ### L2 Update Response | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `l2_updates` | | `symbol` | string | `BTCUSD`, etc. | | `changes` | array of arrays | [side, price, quantity] | ### Trade Response | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `trade` | | `symbol` | string | `BTCUSD`, etc. | | `event_id` | long | Event ID of the trade | | `timestamp` | long | Time in milliseconds | | `price` | string | Price of the trade | | `quantity` | string | Quantity traded | | `side` | string | Taker side: `buy` or `sell` | | `tid` | long | Trade ID | ### Examples Initial L2 response: ```json { "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 } ] } ``` L2 update: ```json { "type": "l2_updates", "symbol": "BTCUSD", "changes": [["sell", "9160.20", "0.1921229751"]] } ``` --- ## Candles Data Feed The Candle Data feed provides periodic updates with OHLCV data for the given timeframe. ### Subscribe | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `subscribe` | | `subscriptions` | array | | | -- `name` | string | `candles_1m`, `candles_5m`, `candles_15m`, `candles_30m`, `candles_1h`, `candles_6h`, `candles_1d` | | -- `symbols` | array | `["BTCUSD", ...]` | ### Response | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `candles_1m_updates`, `candles_5m_updates`, etc. | | `symbol` | string | `BTCUSD`, etc. | | `changes` | array of arrays | [time, open, high, low, close, volume] | ```json { "type": "subscribe", "subscriptions": [ { "name": "candles_15m", "symbols": ["BTCUSD"] } ] } ``` ```json { "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] ] } ``` --- ## Mark Price Feed The mark price feed provides mark price updates for perpetual instruments and select spot pairs. ### Subscribe | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `subscribe` | | `subscriptions` | array | | | -- `name` | string | `mark_price` | | -- `symbols` | array | `["BTCGUSDPERP", "BTCUSD", ...]` | ### Response | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `mark_price_updates` | | `symbol` | string | `BTCGUSDPERP` | | `changes` | array | | Each element of `changes`: | Field Name | Type | Values | | ---------- | ---- | ------ | | `timestamp` | integer | nanoseconds | | `mark_price` | string | mark price | | `spot_index` | string | spot index | ```json { "type": "mark_price_updates", "symbol": "BTCGUSDPERP", "changes": [ { "timestamp": 1673932381478308169, "mark_price": "21154.098", "spot_index": "21175.27333" } ] } ``` --- ## Funding Amount Feed The Funding Amount feed provides funding amount updates for perpetual instruments. ### Subscribe | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `subscribe` | | `subscriptions` | array | | | -- `name` | string | `funding_amount` | | -- `symbols` | array | `["BTC-GUSD-PERP", ...]` | ### Response | Field Name | Type | Values | | ---------- | ---- | ------ | | `type` | string | `funding_amount_updates` | | `symbol` | string | `BTCGUSDPERP` | | `changes` | array | | Each element of `changes`: | 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 | ```json { "type": "funding_amount_updates", "symbol": "BTCGUSDPERP", "changes": [ { "timestamp": 1673932380007696874, "funding_amount": "0", "funding_date_time": 1673932380007696874, "funding_interval_in_minutes": 60, "is_realized": false } ] } ``` --- URL: https://developer.gemini.com/websocket/archived/v1.md # Market Data v1 (Archived) :::warning This documentation is archived for reference only. These APIs have been replaced by the [**new WebSocket API**](/websocket/introduction). **Start new integrations with the new WebSocket API.** ::: # WebSocket API v1 Overview ## Introduction Using WebSockets provides several advantages: - 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](/rest/order-status-apis#get-active-orders) endpoint every five seconds. Using the private [Order Events](/websocket/archived/order-events) API, you would subscribe once and receive real time notifications of all order activity. ### WebSocket Protocol Resources - [About HTML5 WebSocket](https://websocket.org/aboutwebsocket.html) - [RFC 6455 The WebSocket Protocol](https://tools.ietf.org/html/rfc6455) --- ## Requests Both public and private WebSocket API requests begin with a GET request that includes headers asking for an upgrade to the WebSocket protocol. The private API WebSocket request also includes the standard private API headers. ### 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 API 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 Gemini uses API keys to allow access to private APIs. You can obtain these by logging on and creating a key in [Settings/API](https://exchange.gemini.com/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. ### Payload The payload of the requests will be a JSON object. 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. :::info Authenticated APIs do **not** submit their payload as POSTed data, but instead put it in the X-GEMINI-PAYLOAD header. ::: ### 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 | ### Example ```json { "request": "/v1/order/events", "nonce": } ``` Base64 encode, then sign with HMAC-SHA384: ```python import base64, hmac, hashlib, json, time 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() ``` --- ## Roles Gemini uses a role-based system for private API endpoints so that you can separate privileges for your API keys. | Endpoint | URI | Trader | Fund Manager | Auditor | | -------------- | ------------------ | ------ | ------------ | ------- | | Order Events | `/v1/order/events` | Yes | No | Yes | --- ## 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. --- ## Data Types | Type | Description | | ------------- | ----------- | | `string` | A simple quoted string, following standard JSON rules. | | `decimal` | A decimal value, encoded in a JSON string. | | `timestamp` | The number of seconds since 1970-01-01 UTC. Use `timestampms` when available. | | `timestampms` | The number of milliseconds since 1970-01-01 UTC. Transmitted as a JSON number. | | `integer` | A whole number, transmitted as a JSON number. | | `boolean` | A JSON boolean, the literal string `true` or `false`. | | `array` | A JSON array. | ### Timestamps The timestamp data type describes a date and time as a whole number in Unix Time format. :::info Gemini strongly recommends using milliseconds instead of seconds for timestamps. ::: | 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 | In responses, `timestamp` denotes seconds and `timestampms` denotes milliseconds since 1970-01-01 UTC. ### Basis Points Fees are calculated as a fraction of the notional value of each trade (price x amount) in basis points ("bps"), which represent 1/100th of a percent. For example, a fee of 25 bps means 0.25% of the denominated value. --- ## Sequence Numbers Events and heartbeats contain a `socket_sequence` number: 1. WebSocket connection is established 2. Optional subscription acknowledgement 3. First event with `socket_sequence` set to `0` 4. Each subsequent message increases the sequence by one If you see a gap in the sequence number, disconnect and reconnect. :::info Each time you reconnect, the sequence number resets to zero. Multiple WebSocket connections each have separate sequence numbers. ::: --- ## Rate Limits To prevent abuse, Gemini imposes rate limits on incoming requests. For public WebSocket APIs, we recommend that you do not exceed 1 request per symbol per minute. --- ## Error Codes If a response is in error, the HTTP response code will reflect this, and a JSON body will be returned: ```json { "result": "error", "reason": "BadNonce", "message": "Out-of-sequence nonce <1234> precedes previously used nonce <2345>" } ``` ### HTTP Status Codes | HTTP Status | Meaning | | ----------- | ------- | | 200 | Request was successful | | 30x | API entry point has moved, see Location header | | 400 | Market not open, or the request was malformed | | 403 | The API key is missing the role necessary to access this 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 Reasons | Reason | Meaning | | ------ | ------- | | ClientOrderIdTooLong | The Client Order ID must be under 100 characters | | 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 | | 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 | | 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, an invalid side was specified | | InvalidSignature | The signature did not match the expected signature | | InvalidSymbol | An invalid symbol was specified | | MarketNotOpen | The order was rejected because the market is not accepting new orders | | MissingApikeyHeader | The `X-GEMINI-APIKEY` header was missing | | MissingPayloadHeader | The `X-GEMINI-PAYLOAD` header was missing | | MissingSignatureHeader | The `X-GEMINI-SIGNATURE` header was missing | | MissingRole | The API key does not have the required role assigned | | OrderNotFound | The order specified was not found | | RateLimit | Requests were made too frequently | | System | We are experiencing technical issues | --- ## Sandbox Gemini's [sandbox site](https://exchange.sandbox.gemini.com/) is an instance of the Gemini Exchange that offers full exchange functionality using test funds. | Resource | URL | | -------- | --- | | 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 | Go to the [sandbox site](https://exchange.sandbox.gemini.com) to register for a test account. Your account will automatically be credited with test funds ($100,000 USD, 1,000 BTC, 20,000 ETH, 20,000 BCH, 20,000 ZEC, and 20,000 LTC). **Two Factor Authentication**: 2FA is enabled by default. To disable for automated testing, set a cookie or HTTP header named `GEMINI-SANDBOX-2FA` and enter `9999999` as the 2FA code. --- ## Client Order ID Client order ID is a client-supplied order identifier that Gemini will echo back in all subsequent messages about that order. Gemini strongly recommends supplying `client_order_id` when placing orders. Your client order IDs are only visible to Gemini and you. They should be unique per trading session and must match: `[:\-_\.#a-zA-Z0-9]{1,100}`. | Characters | Description | ASCII Codes | | :--------: | ----------- | ----------- | | `A-Z` | Uppercase letters | 65-90 | | `a-z` | Lowercase letters | 97-122 | | `0-9` | Digits | 48-57 | | `# - . : _` | Special characters | 35, 45, 46, 58, 95 | --- # Market Data v1 Market data is a public API that streams all the market data on a given symbol. ## WebSocket Request `wss://api.gemini.com/v1/marketdata/:symbol` ## URL Parameters | Parameter | Required | Default | Description | | --------- | -------- | ------- | ----------- | | `heartbeat` | No | false | Set to `true` to receive a heartbeat every 5 seconds | | `top_of_book` | No | false | If `true`, receive top of book only (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: - To be excluded from `change` events, an entry type must be explicitly flagged `false` - If no filtering parameters are included, all entry types will appear :::info `top_of_book` has no meaning and initial book events are empty when only `trades` is specified. ::: ## Response Each frame contains a JSON message with the following format: | Field | Type | Description | | ----- | ---- | ----------- | | `type` | string | `heartbeat` or `update` | | `socket_sequence` | integer | Monotonic increasing sequence number | Messages of type `update` also include: | Field | Type | Description | | ----- | ---- | ----------- | | `eventId` | integer | Monotonically increasing sequence number for changes | | `events` | array | Order book changes or trade indications | | `timestamp` | timestamp | Timestamp in seconds (use `timestampms` instead) | | `timestampms` | timestampms | Timestamp in milliseconds | All elements of `events` share: | Field | Type | Description | | ----- | ---- | ----------- | | `type` | string | Either `trade` or `change` | ### Change Event | Field | Type | Description | | ----- | ---- | ----------- | | `price` | decimal | Price of this order book entry | | `side` | string | `bid` or `ask` | | `reason` | string | `place`, `trade`, `cancel`, or `initial` | | `remaining` | decimal | Quantity remaining at this price level | | `delta` | decimal | Quantity changed (may be negative) | :::info Every trade triggers a message with entries of both types `trade` and `change`. ::: To keep an up-to-date order book, watch for `{"type": "change"}` events and update the price level at `price` with the amount at `remaining`. ### Trade Event | Field | Type | Description | | ----- | ---- | ----------- | | `price` | decimal | Execution price | | `amount` | decimal | Amount traded | | `makerSide` | string | `bid` or `ask` | ## Examples ```python 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}) ``` ### Initial Response (top of book) ```json { "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" } ] } ``` ### When a trade occurs ```json { "type": "update", "eventId": 5375547515, "timestamp": 1547760288, "timestampms": 1547760288001, "socket_sequence": 15, "events": [ { "type": "trade", "tid": 5375547515, "price": "3632.54", "amount": "0.1362819142", "makerSide": "ask" } ] } ``` ### Heartbeat ```json { "type": "heartbeat", "socket_sequence": 30 } ``` --- URL: https://developer.gemini.com/websocket/archived/order-events.md # Order Events (Archived) :::warning This documentation is archived for reference only. These APIs have been replaced by the [**new WebSocket API**](/websocket/introduction). **Start new integrations with the new WebSocket API.** ::: # 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 receive information about order events like acceptance, booking, fills, cancels, and more. ## WebSocket Request `wss://api.gemini.com/v1/order/events` The API key must have the Trader or Auditor role assigned. :::info Using a Master scoped API key receives event data for all accounts in the group. ::: ### Headers | Header | Value | | ------ | ----- | | `X-GEMINI-APIKEY` | Your Gemini API session key | | `X-GEMINI-PAYLOAD` | Base64-encoded JSON: `{"request": "/v1/order/events", "nonce": 123456}` | | `X-GEMINI-SIGNATURE` | See [Private API Invocation](/websocket/archived/v1#private-api-invocation) | ### URL Parameters | Parameter | Type | Required | Description | | --------- | ---- | -------- | ----------- | | `symbolFilter` | string | No | Optional symbol filter | | `apiSessionFilter` | string | No | Optional API session key filter | | `eventTypeFilter` | string | No | Optional event type filter | | `heartbeat` | boolean | No | Stream heartbeats (default: `false`) | ### Python Example ```python import ssl, websocket, json, base64, hmac, hashlib, time 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&heartbeat=true", on_message=lambda ws, msg: print(msg), 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}) ``` --- ## Response Once your WebSocket session is established, you will receive: 1. A subscription acknowledgement 2. A list of your active orders (unless filtered out) 3. Ongoing order events interspersed with heartbeats every five seconds ### Common Fields These fields are common to all order events except `subscription_ack` and `heartbeat`. | Field | Type | Required | Description | | ----- | ---- | -------- | ----------- | | `type` | string | Yes | Event type: `accepted`, `booked`, `fill`, `cancelled`, etc. | | `socket_sequence` | integer | Yes | Monotonic increasing sequence number | | `order_id` | string | Yes | Order ID assigned by Gemini | | `event_id` | string | Yes* | Event ID (not present for `initial` events) | | `account_name` | string | No | Account name (Master scoped API keys only) | | `api_session` | string | Yes* | API session key (`UI` for website orders) | | `client_order_id` | string | No | Client-specified order ID | | `symbol` | string | Yes | Symbol of the order | | `side` | string | Yes | `buy` or `sell` | | `behavior` | string | No | `immediate-or-cancel`, `fill-or-kill`, or `maker-or-cancel` | | `order_type` | string | Yes | Description of the order | | `timestamp` | string | Yes | Timestamp in seconds (use `timestampms` instead) | | `timestampms` | timestampms | Yes | Timestamp in milliseconds | | `is_live` | boolean | Yes | `true` if the order is active on the book | | `is_cancelled` | boolean | Yes | `true` if the order has been canceled | | `is_hidden` | boolean | Yes | `true` if the order is active but not visible | | `avg_execution_price` | decimal | No | Average execution price (`0` if unfilled) | | `executed_amount` | decimal | No | Amount filled | | `remaining_amount` | decimal | No | Amount remaining (absent for market buys) | | `original_amount` | decimal | No | Original quantity (absent for market buys) | | `price` | decimal | No | Limit order price (absent for market orders) | | `total_spend` | decimal | No | Total spend for market buys | --- ## Event Types | Event Type | Filterable | Description | | ---------- | ---------- | ----------- | | `subscription_ack` | No | Acknowledges your subscription and echoes back parsed filters | | `heartbeat` | Yes | Sent every 5 seconds to show connection is alive | | `initial` | Yes | Current active orders at subscription time | | `accepted` | Yes | Order accepted for initial processing | | `rejected` | Yes | Order rejected | | `booked` | Yes | Order visible on the order book | | `fill` | Yes | Order filled (partial or complete) | | `cancelled` | Yes | Order cancelled | | `cancel_rejected` | Yes | Cancel request could not be fulfilled | | `closed` | Yes | Last event in order lifecycle | ### Subscription Acknowledgement ```json { "type": "subscription_ack", "accountId": 5365, "subscriptionId": "ws-order-events-5365-b8bk32clqeb13g9tk8p0", "symbolFilter": ["btcusd"], "apiSessionFilter": ["UI"], "eventTypeFilter": ["fill", "closed"] } ``` ### Heartbeat ```json { "type": "heartbeat", "timestampms": 1547742998508, "sequence": 31, "trace_id": "b8biknoqppr32kc7gfgg", "socket_sequence": 37 } ``` ### Active Orders (Initial) ```json [ { "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 } ] ``` ### Accepted Your order is now live on the exchange. Possible outcomes: - Immediate fill followed by `booked` (remaining qty) or `closed` (fully filled) - `cancelled` then `closed` (behavior constraints not met) - Market orders: `fill` event(s) followed by `closed` ```json [ { "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 } ] ``` ### Rejected ```json [ { "type": "rejected", "order_id": "104246", "event_id": "104247", "reason": "InvalidPrice", "symbol": "btcusd", "side": "buy", "order_type": "exchange limit", "is_live": false, "original_amount": "5", "price": "703.14444444", "socket_sequence": 310311 } ] ``` ### Booked When limit orders are `booked`, they have a non-zero quantity visible on the exchange. Market orders are never booked. ```json [ { "type": "booked", "order_id": "109535955", "event_id": "109535957", "symbol": "btcusd", "side": "sell", "order_type": "exchange limit", "timestamp": "1547742952", "timestampms": 1547742952725, "is_live": true, "is_cancelled": false, "is_hidden": false, "remaining_amount": "1", "original_amount": "1", "price": "3592.23", "socket_sequence": 25 } ] ``` ### Fill A `fill` event indicates a partial or complete fill. A complete fill is distinguished by `remaining_amount` of `0`. The `fill.price` is the execution price (always present), while `price` is the original limit order price. | Field | Type | Description | | ----- | ---- | ----------- | | `fill.trade_id` | string | Event id the order was filled at | | `fill.liquidity` | string | `Maker` or `Taker` | | `fill.price` | decimal | Execution price | | `fill.amount` | decimal | Amount of the trade fill | | `fill.fee` | decimal | Fee for this side of the trade | | `fill.fee_currency` | string | Currency code of the fee | ```json [ { "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 } ] ``` ### Cancelled Orders may be cancelled because you requested it, behavior constraints couldn't be fulfilled, FIX connection was disconnected, or heartbeat timeout. | Field | Type | Description | | ----- | ---- | ----------- | | `cancel_command_id` | string | Event id of the cancel command | | `reason` | string | Reason for cancellation | ```json [ { "type": "cancelled", "order_id": "109944118", "event_id": "109964524", "cancel_command_id": "109964523", "reason": "Requested", "symbol": "bchusd", "side": "buy", "order_type": "exchange limit", "is_live": false, "is_cancelled": true, "socket_sequence": 22 } ] ``` ### Cancel Rejected ```json [ { "type": "cancel_rejected", "order_id": "6425", "event_id": "6434", "cancel_command_id": "6433", "reason": "OrderNotFound", "symbol": "btcusd", "side": "buy", "socket_sequence": 312300 } ] ``` ### Closed The `closed` event is the last event in the lifecycle of any order that has been `accepted`. Your order has been removed from the book. ```json [ { "type": "closed", "order_id": "109535955", "event_id": "109535971", "symbol": "btcusd", "side": "sell", "order_type": "exchange limit", "is_live": false, "is_cancelled": false, "avg_execution_price": "3592.23", "executed_amount": "1", "remaining_amount": "0", "original_amount": "1", "price": "3592.23", "socket_sequence": 82 } ] ``` --- ## Filtering Filtering is completely optional. If you don't specify any filters, you'll see all your order events for every symbol, every API session and the UI, every event type. Filtering works by whitelisting. You can filter on any combination of: 1. One or more supported symbols 2. One or more API session keys (use `UI` for website orders) 3. One or more event types To provide multiple arguments, repeat the parameter: ``` wss://api.gemini.com/v1/order/events?symbolFilter=btcusd&symbolFilter=ethbtc&eventTypeFilter=fill&eventTypeFilter=closed ``` :::info You cannot filter out heartbeat events or your subscription acknowledgement. ::: --- ## Workflow 1. Client submits order to Gemini exchange 2. Is the order accepted? - **Yes**: Gemini sends `accepted`, then zero or more `fill` events - If remaining quantity: `booked` event, order rests until cancelled or filled - If fully filled: `closed` event - **No**: Gemini sends `rejected`, no further events --- URL: https://developer.gemini.com/websocket/archived/multi-market-data.md # Multi Market Data (Archived) :::warning This documentation is archived for reference only. These APIs have been replaced by the [**new WebSocket API**](/websocket/introduction). **Start new integrations with the new WebSocket API.** ::: # Multi Market Data Multi market data is a public API which allows multiple symbols to be streamed via a single endpoint. ## WebSocket Request `wss://api.gemini.com/v1/multimarketdata?symbols=BTCUSD,ETHUSD` ## URL Parameters | Parameter | Required | Default | Description | | --------- | -------- | ------- | ----------- | | `symbols` | Yes | - | Symbols to stream (comma-separated) | | `heartbeat` | No | false | Set to `true` for heartbeats every 5 seconds | | `top_of_book` | No | false | If `true`, receive top of book only | | `bids` | No | true | Include bids in `change` events | | `offers` | No | true | Include asks in `change` events | | `trades` | No | true | Include `trade` events | ## Response | Field | Type | Description | | ----- | ---- | ----------- | | `type` | string | `heartbeat` or `update` | | `socket_sequence` | integer | Monotonic increasing sequence number | Messages of type `update` also include: | Field | Type | Description | | ----- | ---- | ----------- | | `eventId` | integer | Monotonically increasing sequence number | | `events` | array | Order book changes or trade indications | | `timestamp` | timestamp | Timestamp in seconds | | `timestampms` | timestampms | Timestamp in milliseconds | All elements of `events` share: | Field | Type | Description | | ----- | ---- | ----------- | | `type` | string | `trade` or `change` | | `symbol` | string | Symbol (e.g. `BTCUSD`, `ETHUSD`) | ### Change Event | Field | Type | Description | | ----- | ---- | ----------- | | `price` | decimal | Price of this order book entry | | `side` | string | `bid` or `ask` | | `reason` | string | `place`, `trade`, `cancel`, or `initial` | | `remaining` | decimal | Quantity remaining at this price level | | `delta` | decimal | Quantity changed | ### Trade Event | Field | Type | Description | | ----- | ---- | ----------- | | `price` | decimal | Execution price | | `amount` | decimal | Amount traded | | `makerSide` | string | `bid` or `ask` | ## Examples ```python 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}) ``` ### Trade Event ```json { "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" } ] } ``` --- URL: https://developer.gemini.com/trading/websocket/streams.md # Stream Reference Prediction Markets uses the core book, depth, trade, order, balance, and position stream protocol. Prediction Markets WebSocket streams follow the standard Gemini WebSocket protocol. Symbols use the prediction markets format (e.g. `GEMI-BTC05M2606011000-UP`). Replace example symbols with active `instrumentSymbol` values from `GET /v1/prediction-markets/events`. Use WebSocket streams for active trading and market making. Use REST endpoints for event discovery and state reconciliation. ## Stream Matrix | Stream | Auth | Use for | Start here | |----|----|----|----| | [`{symbol}@bookTicker`](#book-ticker) | Public | Best bid/ask prices and quantities | First price watcher | | [`{symbol}@depth5`, `{symbol}@depth10`, `{symbol}@depth20`](#l2-partial-depth-streams) | Public | Periodic top-of-book snapshots | Simple dashboards | | [`{symbol}@depth`, `{symbol}@depth@100ms`](#l2-differential-depth-streams) | Public | Maintaining a local order book | Market making | | [`{symbol}@trade`](#trade-stream) | Public | Recent executions | Trade tape and analytics | | [`{agency}:{index}@indexPrice`](#index-keyed-price-streams) | Public | Price reference feeds for prediction-market contracts | Contract pricing and settlement | | [`orders@account`](#order-events) | Authenticated | Order lifecycle events | Order state tracking | | [`balances@account`](#balance-updates) | Authenticated | Balance changes | Risk checks | | [`positions@account`](#position-updates) | Authenticated | Low-latency position and settlement deltas | Exposure and settlement tracking | | [`contractStatus`](#contract-status) | Public | Contract status and strike updates | Lifecycle monitors | :::note Authenticated streams require Gemini authentication headers (HMAC or OAuth bearer token) during the WebSocket handshake. Browser WebSocket clients cannot set custom headers; use backend services or proxy servers for account streams. ::: ## Index-Keyed Price Streams Index-keyed price streams provide the live reference price associated with a prediction-market contract's `sourceDetails.agency` and `sourceDetails.index`. Subscribe using the exact values returned by the contract or event discovery API: ```text {agency}:{index}@indexPrice ``` For example: ```text lukka:LS-LRRR-ZECUSD@indexPrice lukka:LS-LRRR-HYPE22USD@indexPrice ``` These streams are public and event-driven. The server resolves the `agency`/`index` pair to the corresponding price feed and delivers the reference-price update using the standard third-party price message format. The matching is case-insensitive, but clients should use the values exactly as returned by the source-details fields. Unknown pairs are rejected as invalid stream names. ## Book Ticker | Schema | Frequency | Description | |----|----|----| | `{symbol}@bookTicker` | Real-time | Real time updates to the best bid/ask price for an order book. | ```json { "u": 1751505576085, "E": 1751508438600117161, "s": "GEMI-BTC05M2606011000-UP", "b": "0.48", "B": "5000", "a": "0.52", "A": "3200" } ``` | Field | Type | Description | |-------|--------|--------------------------| | `u` | number | Update ID | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `b` | string | Best bid price | | `B` | string | Best bid quantity | | `a` | string | Best ask price | | `A` | string | Best ask quantity | | `c` | string | Last trade price (present once the book has traded; omitted otherwise) | | `C` | string | Last trade size (present once the book has traded; omitted otherwise) | --- ## L2 Partial Depth Streams | Schema | Frequency | Description | |----|----|----| | `{symbol}@depth5` | Periodic (1s) | Periodic snapshot of the top 5 levels once per second | | `{symbol}@depth10` | Periodic (1s) | Top 10 levels | | `{symbol}@depth20` | Periodic (1s) | Top 20 levels | | `{symbol}@depth5@100ms` | Periodic (100ms) | Top 5 levels every 100 milliseconds | | `{symbol}@depth10@100ms` | Periodic (100ms) | Top 10 levels | | `{symbol}@depth20@100ms` | Periodic (100ms) | Top 20 levels | ```json { "lastUpdateId": 12345678, "bids": [ ["0.26", "5000"], ["0.25", "2000"] ], "asks": [ ["0.28", "3200"], ["0.29", "1500"] ] } ``` | Field | Type | Description | |----------------|----------|------------------------------| | `lastUpdateId` | number | Last update ID | | `bids` | array | Array of [price, quantity] | | `asks` | array | Array of [price, quantity] | :::note Use a depth snapshot as the starting point for any local order book or dollar calculation, then apply differential depth updates to keep it current. Each level is `[price, quantity]`. Public depth for event contracts is normalized in YES space: for YES notional, compute `yesPrice * quantity`; for NO notional, compute `(1 - yesPrice) * quantity`. ::: --- ## L2 Differential Depth Streams | Schema | Frequency | Description | |----|----|----| | `{symbol}@depth` | Periodic (1s) | List of all changed price levels in the last second | | `{symbol}@depth@100ms` | Periodic (100ms) | In the last 100 milliseconds | :::tip[Initial Snapshot] Use the [`snapshot` connection parameter](/prediction-markets/websocket/introduction#snapshot-parameter) to receive an initial orderbook snapshot when subscribing. Connect with `wss://ws.gemini.com?snapshot=-1` for a full snapshot, or specify a positive number for top N levels. ::: :::note Quantity zero indicates price level removal. ::: There is no separate snapshot message and no `lastUpdateId` field on this stream. When you subscribe with the `snapshot` parameter set to a non-zero value, the first `depthUpdate` frame you receive *is* the snapshot — it carries the full requested book state in its `b`/`a` arrays instead of a delta. Its `U` and `u` identify the first and last update IDs covered by the snapshot; use `u` as the last applied update ID. Every frame after that first one is a normal incremental delta: apply it directly to your local book. If a later frame's `U` skips ahead of the last applied `u`, discard the local book and resubscribe to resync. Without the `snapshot` parameter, or with it set to `0`, every frame you receive (including the first) is an incremental delta. ```json { "e": "depthUpdate", "E": 1751508260659505382, "s": "GEMI-BTC05M2606011000-UP", "U": 12345677, "u": 12345678, "b": [ ["0.48", "5000"], ["0.47", "0.00"] ], "a": [ ["0.52", "3200"] ] } ``` | Field | Type | Description | |-------|--------|------------------------------------| | `e` | string | Event type ("depthUpdate") | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `U` | number | First update ID in this event | | `u` | number | Last update ID in this event | | `b` | array | Bid updates [price, quantity] | | `a` | array | Ask updates [price, quantity] | --- ## Trade Stream | Schema | Frequency | Description | |----|----|----| | `{symbol}@trade` | Real-time | Real time trade executions | ```json { "E": 1759873803503023900, "s": "GEMI-BTC05M2606011000-UP", "t": 2840140956529623, "p": "0.50", "q": "10", "m": true } ``` | Field | Type | Description | |-------|---------|--------------------------| | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `t` | number | Trade ID | | `p` | string | Price | | `q` | string | Quantity | | `m` | boolean | Is buyer the maker | --- ## Order Events :::warning Requires an authenticated session ::: | Schema | Frequency | Description | |----|----|----| | `orders@account` | Real-time | Real time order activity for the account associated with the authenticated API key | | `orders@session` | Real-time | Real time order activity for the authenticated API key | Order Event - New: ```json { "e": "orderUpdate", "E": 1759291847686856569, "s": "GEMI-BTC05M2606011000-UP", "i": 73797746498585286, "c": "btc-5m-quote-001", "S": "BUY", "o": "LIMIT", "X": "NEW", "O": "YES", "p": "0.48000", "q": "10", "z": "10", "T": 1759291847686856569 } ``` Order Event - Canceled: ```json { "e": "orderUpdate", "E": 1759291847731455006, "s": "GEMI-BTC05M2606011000-UP", "i": 73797746498585286, "c": "btc-5m-quote-001", "X": "CANCELED", "T": 1759291847731455006 } ``` | Field | Type | Description | |-------|---------|-------------------------| | `e` | string | Event type (`orderUpdate`) | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `i` | number | Order ID | | `c` | string | Client order ID. For RFQ maker fills, this is the `clientId` supplied to `rfq.submit_quote`, or Gemini's deterministic RFQ client order ID when omitted. | | `S` | string | Side, `BUY / SELL` | | `o` | string | Type, `LIMIT / MARKET / STOP_LIMIT / STOP_MARKET` | | `X` | string | Status, `NEW / OPEN / FILLED / PARTIALLY_FILLED / CANCELED / REJECTED / MODIFIED` | | `O` | string | Event outcome, `YES / NO` | | `p` | string | Order price | | `P` | string | Stop price (`0` when not a stop order) | | `q` | string | Original quantity | | `z` | string | Remaining quantity | | `Z` | string | Executed quantity. For `FILLED` / `PARTIALLY_FILLED` events, this is the quantity filled in the last execution. For `CANCELED` and other events, this is the cumulative quantity filled over the lifetime of the order. Use `Z` (not the order status) to determine how much filled — e.g. a fully-filled `IOC` terminates as `CANCELED`. | | `L` | string | Last execution price | | `t` | number | Trade ID | | `n` | string | Fee amount (only present in 'FILLED' events) | | `m` | boolean | Maker flag on fills: `true` = maker, `false` = taker (present on fills only) | | `r` | string | Rejection reason | | `T` | number | Update time (nanoseconds) | :::note Fields with empty or zero values may be omitted from the event. ::: :::note Post-only and immediate time-in-force orders are **accepted, then cancelled** — they are never `REJECTED`: - `MOC` (maker-or-cancel / post-only): if it would take liquidity, the order is cancelled with `MakerOrCancelWouldTake` and never fills. - `IOC` (immediate-or-cancel): fills whatever crosses immediately, then cancels the remainder with `ImmediateOrCancelWouldPost`. A fully-filled `IOC` still ends with a `CANCELED` event — **so determine what filled from the executed quantity (`Z`), never from the final order status.** - `FOK` (fill-or-kill): fills completely and immediately, or is cancelled in full with `FillOrKillWouldNotFill` (no partial fills). `order.place` for these still returns a `200` response with an initial `NEW`; a true rejection returns a non-`200` status with an error code. ::: #### Rejection Reasons When an order is `REJECTED`, the `r` field contains one of: | Reason | Description | |--------|-------------| | `MarketNotOpen` | Market is closed or paused | | `InsufficientFunds` | Account lacks sufficient balance | | `InvalidPrice` | Price must be between $0.01–$0.99 | | `LimitPriceOffTick` | Price does not align with tick size | | `InvalidQuantity` | Quantity below minimum or off increment | | `InvalidTotalSpend` | Total spend calculation error | | `DuplicateOrder` | Duplicate client order ID | | `InsufficientLiquidity` | Not enough liquidity at price | | `UnknownInstrument` | Trading pair does not exist | | `TERMS_NOT_ACCEPTED` | Latest Prediction Markets terms not accepted. Use the REST terms endpoints to read, check, and accept terms before retrying. | #### Cancellation Reasons When an order is `CANCELED` by the system, the `r` field contains one of: | Reason | Description | |--------|-------------| | `SelfCrossPrevented` | Self-trade prevention triggered | | `FillOrKillWouldNotFill` | FOK order could not fill completely | | `ImmediateOrCancelWouldPost` | IOC order would post to book | | `MakerOrCancelWouldTake` | MOC order would take liquidity | | `AuctionCancelled` | Auction-related cancellation | | `ExceedsPriceLimits` | Price moved beyond limits | --- ## Balance Updates :::warning Requires an authenticated session ::: | Schema | Frequency | Description | |----|----|----| | `balances@account` | Real-time | Real time balance updates for the account associated with the authenticated API key | | `balances@account@1s` | Periodic (1s) | Periodic snapshot of all balances every second for the account associated with the authenticated API key | The `balances@account` stream pushes updates in real time whenever a balance change occurs, and only includes the assets that changed. The `balances@account@1s` stream sends a complete snapshot of all account balances every second, regardless of whether they changed. On subscribe, `balances@account@1s` will immediately send the current balances if available. Balance Update: ```json { "e": "balanceUpdate", "E": 1768250434780000000, "u": 1768250421600000000, "B": [ { "a": "USD", "f": "207.39", "c": "207.39" } ] } ``` | Field | Type | Description | |-------|---------|-------------------------| | `e` | string | Event type ("balanceUpdate") | | `E` | number | Event time (nanoseconds) | | `u` | number | Time of the last account update (nanoseconds) | | `B` | array | Balance updates | | `a` | string | Asset code | | `f` | string | Available balance (amount available to trade) | | `c` | string | Confirmed balance (total balance including pending) | --- ## Position Updates :::warning Requires an authenticated session ::: | Schema | Frequency | Description | |----|----|----| | `positions@account` | Real-time | Real time event-contract position updates for the account associated with the authenticated API key | | `positions@account@1s` | Periodic (1s) | Periodic snapshot of all open event-contract positions every second for the account associated with the authenticated API key | The `positions@account` stream pushes deltas in real time on fill, position open/close, and event-contract settlement events, and only includes the rows that changed. A settlement close is delivered as a terminal row with `position` set to `"0"`, followed by a `settlement_payout` amount carrying the payout currency and outcome. The `positions@account@1s` stream sends a complete snapshot of all open positions every second, regardless of whether they changed. On subscribe, `positions@account@1s` will immediately send the current positions if available. Use `positions@account` for low-latency event-contract position deltas on the same WebSocket connection you use for trading. Reconcile with `POST /v1/prediction-markets/positions` after reconnects, missed messages, and settlement windows. :::note Connect to `wss://ws.gemini.com`. Authentication must be provided on the WebSocket upgrade — you cannot authenticate after the connection is established. Use either HMAC-signed API key headers (account-scoped only; master/group keys are rejected with HTTP 401) or an OAuth 2.0 bearer token (`Authorization: Bearer `). See [Authentication](/prediction-markets/websocket/authentication). ::: Subscribe after the connection opens: ```json { "id": "1", "method": "SUBSCRIBE", "params": ["positions@account"] } ``` Server acknowledgement: ```json { "id": "1", "status": 200 } ``` Snapshot and delta frames share the same shape: ```json { "e": "positionReport", "E": 1760000000000000000, "u": 1759999999000000000, "A": 12345, "P": [ { "t": "ec", "s": "GEMI-BTC05M2606011000-UP", "a": [ { "t": "position", "v": "2.5" } ] } ] } ``` | Field | Type | Description | |-------|------|-------------| | `e` | string | Event type, always `positionReport` | | `E` | number | Event timestamp in nanoseconds | | `u` | number | Last account update timestamp in nanoseconds | | `A` | number | Account ID | | `P` | array | Position rows for this account; an empty array means no open position rows are included | | `P[].t` | string | Product type. Event contracts use `ec` | | `P[].s` | string | Instrument symbol | | `P[].a` | array | Named amount array | | `P[].a[].t` | string | Amount label. Position quantity uses `position`; a settlement close also includes `settlement_payout` | | `P[].a[].v` | string | Decimal string amount. `position` is signed; `settlement_payout` is the payout amount | | `P[].a[].c` | string | Optional asset code. Settlement payouts use `usd`; position quantities omit this field | | `P[].a[].o` | string | Settlement outcome on `settlement_payout`: `YES`, `NO`, or `UNSPECIFIED` | :::note The first subscription for an account returns a snapshot of currently open positions. Subsequent frames are deltas carrying only rows that changed. A position close emits a row with `position` value `"0"` before that row is evicted; a settlement close includes `settlement_payout` and outcome in that same terminal row. Later snapshots omit zero-position rows. The `a` array is intentionally extensible; clients should ignore unknown amount labels instead of failing. ::: --- ## Settlements :::warning Requires an authenticated session ::: There is no standalone `settlements@account` stream. Settlement details are delivered in the terminal `positionReport` delta on `positions@account`. The terminal row contains a zero `position` amount and a `settlement_payout` amount. The payout amount uses `c: "usd"`; its `o` value is `YES`, `NO`, or `UNSPECIFIED`. Subscribe using the standard [Position Updates](#position-updates) request format. Use `POST /v1/prediction-markets/positions/settled` for historical settled positions and reconciliation after reconnects or missed messages. --- ## Contract Status Prediction-market contract lifecycle events — status transitions (e.g. `Awaiting Approval` → `Approved` → `Active`) and strike-populated moments for Up/Down contracts. | Schema | Frequency | Description | |----|----|----| | `contractStatus` | Real-time | Status changes and strike-price updates for prediction-market contracts | ```json # Strike-based contract (e.g. HI78999D63) { "e": "contractStatus", "E": 1776871540195, "s": "gemi-btc15m2604221545-hi78999d63", "k": "btc15m2604221545", "c": "HI78999D63", "i": 134794, "p": "78999.63", "o": "Awaiting Approval", "n": "Approved" } # Up/Down contract (no numeric strike — `p` omitted until populated) { "e": "contractStatus", "E": 1776871295498, "s": "gemi-btc05m2604221630-up", "k": "btc05m2604221630", "c": "UP", "i": 134791, "o": "Awaiting Approval", "n": "Approved" } ``` | Field | Type | Description | |-------|--------|-------------| | `e` | string | Event type (`contractStatus`) | | `E` | number | Event time (Unix milliseconds) | | `s` | string | Instrument symbol | | `k` | string | Event ticker | | `c` | string | Contract ticker (e.g. `HI78999D63`, `UP`, `DOWN`) | | `i` | number | Contract ID | | `p` | string | Strike price parsed from the contract ticker. Omitted for Up/Down contracts until the strike is set at activation | | `o` | string | Previous status | | `n` | string | New status | :::note For Up/Down contracts, `p` is omitted while the strike is unknown and included once it is set — subscribers can detect strike availability by the field's presence. ::: --- URL: https://developer.gemini.com/trading/websocket/message-format.md # Message Format Our WebSocket API uses JSON-formatted messages for all communication. ### Request Format All requests follow a consistent structure: ```json { "id": "1", "method": "METHOD_NAME", "params": {...} } ``` | Field | Type | Required | Description | |----------|-------------------|----------|------------------------------------------------| | `id` | string \| number | Yes | Unique identifier for matching request/response | | `method` | string | Yes | The method to invoke | | `params` | object \| array | No | Method parameters (varies by method) | ### Response Format Successful responses include the request ID and result: ```json { "id": "1", "status": 200, "result": {...} } ``` | Field | Type | Description | |----------|------------------|------------------------------------------| | `id` | string \| number | Matches the request ID | | `status` | number | HTTP status code | | `result` | any | Method-specific response data | ### Error Response Error responses include error details: ```json { "id": "1", "status": 401, "error": { "code": -1002, "msg": "Authentication required" } } ``` | Field | Type | Description | |-----------------|------------------|---------------------------------| | `id` | string \| number | Matches the request ID | | `status` | number | HTTP status code | | `error.code` | number | Internal error code | | `error.msg` | string | Human-readable error message | ### Error Codes | Code | HTTP Status | Description | |--------|-------------|-------------------------------| | -1000 | 500 | Internal server error | | -1002 | 401 | Authentication required | | -1003 | 429 | Rate limit exceeded | | -1013 | 400 | Invalid parameters | | -1020 | 400 | Unsupported operation | | -2010 | 400 | Order rejected | ### Event Types Streaming events carry an `e` field that identifies the event type, so a single connection can demultiplex every subscription: | `e` value | Stream | |-----------|--------| | `depthUpdate` | L2 differential depth (`{symbol}@depth`, `{symbol}@depth@100ms`) | | `orderUpdate` | Order events (`orders@account`, `orders@session`) | | `balanceUpdate` | Balance updates (`balances@account`, `balances@account@1s`) | | `positionReport` | Position updates (`positions@account`, `positions@account@1s`) | | `contractStatus` | Contract status (`contractStatus`) | The Book Ticker (`{symbol}@bookTicker`), L2 Partial Depth (`{symbol}@depth5` / `@depth10` / `@depth20`), and Trade (`{symbol}@trade`) payloads do **not** carry an `e` field — identify those by the stream you subscribed to. :::note New event types may be added over time. Treat any `e` value you do not recognize as a forward-compatible addition and ignore it. ::: --- URL: https://developer.gemini.com/trading/websocket/introduction.md # Introduction **Version:** 0.10.7 • **Status:** Production • **Public URL:** `wss://ws.gemini.com` Our WebSocket API provides low latency access to real-time market data and order execution for professional traders and institutions. Built from the ground up for performance, our WebSocket API delivers fastest latency on AWS with enterprise-grade reliability. :::tip [**Try It Now** with our interactive documentation](/trading/websocket/playground#method-subscribe) ::: ### Key Features - **Low Latency** - Sub-10ms market data updates for competitive advantage - **Real-Time Trading** - Place, modify, and cancel orders via WebSocket - **Multiple Streams** - Subscribe to multiple markets simultaneously ### Performance Tiers | Tier | Target | Description | |------|---------------------|-------------| | **Tier 2** _(Public Internet)_ | p99~15ms | Public offering connecting to **AWS us-east-1 over the public internet**. Provides good **baseline performance** with minimal setup complexity. | | **Tier 1** _(In Region)_ | p99~10ms | **Direct connection** to us-east-1 feed. Provides **improved performance** a step above the public offering but requires onboarding to peer to our infrastructure. | | **Tier 0** _(Local Zone)_ | p99~5ms | **Best performance** outside of NY5, physically closest to our data center. Requires onboarding similar to us-east-1. | :::info Please email api@gemini.com to onboard to our WebSocket high performance tiers. ::: ### Connection Parameters Connection-level query parameters can be passed in the WebSocket URL to customize behavior: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `snapshot` | integer | `0` | Controls initial orderbook snapshot delivery when subscribing to differential depth streams | | `cancelOnDisconnect` | boolean | `false` | Automatically cancel all open orders when the WebSocket session disconnects | #### Snapshot Parameter The `snapshot` parameter controls whether an initial orderbook snapshot is sent when subscribing to [differential depth streams](/trading/websocket/streams#l2-differential-depth-streams) (`{symbol}@depth`, `{symbol}@depth@100ms`). **Connection URL example:** ``` wss://ws.gemini.com?snapshot=-1 ``` | `snapshot` Value | Behavior | |------------------|----------| | Not specified / `0` | No snapshot — only incremental updates (default) | | `-1` (or any negative) | Full orderbook snapshot sent immediately on subscribe | | Positive integer (e.g., `100`) | Top N levels snapshot sent on subscribe | :::tip Use `snapshot=-1` to receive a full orderbook snapshot immediately when subscribing to differential depth streams. This is useful for initializing your local orderbook state without needing a separate REST API call. ::: #### Cancel On Disconnect Parameter The `cancelOnDisconnect` parameter enables automatic cancellation of all your open orders when the WebSocket session disconnects. This is a safety feature that helps prevent unwanted exposure from stale orders if your connection drops unexpectedly. **Connection URL example:** ``` wss://ws.gemini.com?cancelOnDisconnect=true ``` | `cancelOnDisconnect` Value | Behavior | |----------------------------|----------| | Not specified / `false` | Orders remain active after disconnect (default) | | `true` | All open orders are automatically cancelled on disconnect | :::warning When `cancelOnDisconnect=true` is enabled, **all** open orders placed via the WebSocket session will be cancelled upon disconnection, including orders that may have been intentionally left open. Ensure your trading strategy accounts for this behavior. ::: :::tip Combine multiple connection parameters using `&`: `wss://ws.gemini.com?snapshot=-1&cancelOnDisconnect=true` ::: --- URL: https://developer.gemini.com/trading/websocket/authentication.md # Authentication Prediction Markets uses the core WebSocket authentication protocol. Product access and terms acceptance are separate prerequisites. ## Generate an API Key API keys for our WebSocket API have special requirements: 1. Navigate to [API Settings](https://exchange.gemini.com/settings/api) 2. Click **"Create API key"** 3. **Scope:** Select the account you want to trade with 4. **Settings:** - Enable **"Uses a time-based nonce"** - Select **Trader** for trading methods such as `order.place`; **Auditor** or **Trader** is sufficient for `positions@account` 5. Save your API key and secret securely :::warning Only **account-scoped** keys with **time-based** nonces are accepted. Account keys use the `account-...` prefix. Master or group keys, such as `master-...` keys, are rejected with HTTP 401. ::: --- ## Create an Authenticated Connection Connect to `wss://ws.gemini.com` and pass the following headers when establishing the WebSocket connection: | Header | Value | |--------|-------| | `X-GEMINI-APIKEY` | Your account-scoped Gemini API key | | `X-GEMINI-NONCE` | Decimal nonce | | `X-GEMINI-PAYLOAD` | `base64(string(nonce))` | | `X-GEMINI-SIGNATURE` | `hex(hmac_sha384(payload, api_secret))` | :::note Auditor or Trader role is sufficient for `positions@account`. Trader role is required for trading operations such as `order.place`. ::: :::note Before sending trading orders, check `GET /v1/prediction-markets/terms/status`. If `hasAcceptedLatest` is `false`, display `GET /v1/prediction-markets/terms` and accept with `POST /v1/prediction-markets/terms/accept`, then retry the order. ::: :::warning Authentication headers must be provided during the initial WebSocket handshake. You cannot authenticate after the connection is established. ::: ### Signature Generation Step-by-Step ```text # Create a monotonically increasing integer nonce. # Unix seconds or Unix milliseconds are both accepted, but the value must # increase across connections for the same key. nonce = current_unix_timestamp_in_seconds_or_milliseconds # Base64 encode the string form of the nonce. payload = base64_encode(string(nonce)) # Generate a signature using the hmac_sha384 algorithm. signature = hmac_sha384(payload, api_secret) # Convert the signature to hex so it can be passed in the headers. hexSignature = hex(signature) ``` --- ## Alternative: OAuth 2.0 Bearer Token If your application uses [OAuth 2.0](/authentication/oauth) to access the Gemini API, you can authenticate the WebSocket connection with the same access token instead of provisioning an API key. Pass the access token in the `Authorization` header on the WebSocket upgrade request: | Header | Value | |-----------------|--------------------------| | `Authorization` | `Bearer ` | When using OAuth, you do **not** send the `X-GEMINI-APIKEY`, `X-GEMINI-NONCE`, `X-GEMINI-PAYLOAD`, or `X-GEMINI-SIGNATURE` headers. :::note The access token must include a position scope for `positions@account`: `positions:read` or `predictions:positions:read`. See [OAuth scopes](/authentication/oauth#oauth-scopes). ::: :::warning Access tokens are short-lived (default 24 hours). If the token expires during a session, the server will close the connection and you must reconnect with a refreshed token — tokens cannot be rotated on a live connection. See [Using Refresh Tokens](/authentication/oauth#using-refresh-tokens). ::: --- URL: https://developer.gemini.com/trading/rest-api/staking.md # Staking import { IconInfoOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/trading/rest-api/orders.md # Orders import { IconPlusCircleOutlined, IconClearOutlined, IconGraphLine, IconMenu, IconInfoOutlined, IconDocumentOutlined, IconClockOutlined, IconReceipt, IconGraphCandle, IconChartAreaBaseline, } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/trading/rest-api/market-data.md # Market Data import { IconMenu, IconInfoOutlined, IconChartLine, IconChartCandle, IconChartBar, IconStaticOrderBook, IconReceipt, IconUsd, IconDocumentOutlined, } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/trading/rest-api/margin.md # Margin Trading import { IconInfoOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/trading/rest-api/instant-orders.md # Instant Orders import { IconInfoOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/trading/rest-api/fund-management.md # Fund Management import { IconWalletOutlined, IconUsd, IconDocumentOutlined, IconPlusCircleOutlined, IconReceipt, IconClockOutlined, IconDeleteOutlined, IconInfoOutlined, IconMenu, IconCheckCircleOutlined, IconChartLine } from "@hubble/icons/web"; --- URL: https://developer.gemini.com/trading/rest-api/derivatives.md # Perpetuals import { IconDocumentOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/trading/rest-api/clearing.md # Clearing import { IconPlusCircleOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/tools/typescript-sdk/websocket.md # TypeScript SDK — WebSocket The SDK provides real-time market data and account updates over WebSocket. Public streams require no authentication. Private streams and request methods (orders, balances, positions, RFQ deliveries, and order actions) require server-side WebSocket authentication and run only from the server entry point. ## Public and private surfaces The client makes the authentication boundary explicit in its API. A single server client can expose both surfaces, but they use separate WebSocket connections and never share frames or connection state. | Surface | Available from | Authentication | Connection | Includes | | --- | --- | --- | --- | --- | | `client.websocket.public` | Browser and server | None | Shared public session | Trades, book tickers, depth updates, contract status, public RFQ discovery, and public controls | | `client.websocket.private` | Server only | HMAC or confidential OAuth | Separate authenticated session | Account/session orders, balances, positions (including terminal settlement details), RFQ deliveries, order actions, and RFQ quote mutations | | `client.websocket.public.orderBook()` | Browser and server | None | Separate shared order-book session | Self-healing order books and resync events | | `client.websocket.public.depth()` | Browser and server | None | One isolated session per snapshot stream | Partial-depth snapshots | Use the public surface for information Gemini makes available to everyone. Use the private surface for account-specific data or any operation that can change state. The browser entry point intentionally does not expose `.private`; OAuth in a browser authenticates REST only. See [Authentication](/tools/typescript-sdk/authentication#browser-oauth-pkce) for why. ## Public streams Subscribe to market data without authentication: ```ts import { createClient } from "@gemini-markets/sdk/browser"; const client = createClient({ env: "sandbox" }); // Real-time trades const trades = client.websocket.public.trades("BTCUSD"); trades.on("message", (trade) => { console.log(trade.p, trade.q, trade.m); // price, quantity, is-maker }); await trades.ready; // wait for subscription confirmation // Book ticker (best bid/ask) const ticker = client.websocket.public.bookTicker("BTCUSD"); ticker.on("message", (tick) => console.log(tick)); // Depth updates (order book diffs) const depthUpdates = client.websocket.public.depthUpdates("BTCUSD"); depthUpdates.on("message", (update) => console.log(update)); // Depth snapshots (top N levels) const depthSnapshot = client.websocket.public.depth("BTCUSD", { levels: 10 }); depthSnapshot.on("message", (snapshot) => console.log(snapshot)); ``` Public streams share a single underlying WebSocket connection. Opening multiple streams to different symbols reuses the same session. ### Closing a stream ```ts await trades.close(); // sends unsubscribe, waits for acknowledgement ``` Closing one stream does not affect others on the shared session. Call `client.close()` to shut down all streams and connections. ## Live order book The SDK maintains a self-healing L2 order book from WebSocket depth data: ```ts const book = client.orderBook("BTCUSD"); book.on("update", (lob, delta) => { console.log("Best bid:", lob.bestBid()); console.log("Best ask:", lob.bestAsk()); console.log("Spread:", lob.spread()); console.log("Mid:", lob.mid()); console.log("Top 5 bids:", lob.topN("bids", 5)); console.log("Changed levels:", delta); }); book.on("resync", () => { // A gap was detected in the update stream. // The book is rebuilding from a fresh snapshot. // Treat current state as stale until the next "update" event. console.warn("Order book resyncing — data may be stale"); }); book.on("error", (err) => console.error(err)); ``` The first `"update"` event after subscribing (or after a `"resync"`) carries the **full book** — treat it as a replacement, not an incremental diff. Subsequent updates carry only changed levels; a quantity of `"0"` means the level was removed. For exact decimal arithmetic, use `spreadDecimal()` and `midDecimal()` which return string decimals (`"0.01"`). `spread()` and `mid()` return floating-point numbers for display only. ```ts book.close(); // stop updates and release the stream ``` ## Authenticated streams Authenticated streams require the server entry point and an auth strategy. They use the `ws` package to set custom headers on the WebSocket upgrade request. ```ts import { createClient, HmacAuth } from "@gemini-markets/sdk/server"; const client = await createClient({ env: "sandbox", auth: new HmacAuth({ apiKey, apiSecret }), }); // Order updates for the current session const orders = client.websocket.private.orders({ scope: "session" }); orders.on("message", (order) => { console.log(order.i, order.X, order.z); // orderId, status, remainingQuantity }); // Account-wide balance updates const balances = client.websocket.private.balances(); balances.on("message", (update) => { for (const b of update.B) { console.log(b.a, b.f, b.c); // asset, free, locked } }); // Prediction-market positions and terminal settlement details const positions = client.websocket.private.positions(); positions.on("message", (report) => { for (const row of report.P) { const payout = row.a.find((amount) => amount.t === "settlement_payout"); if (payout) console.log(row.s, payout.o, payout.v, payout.c); } }); // Use intervalMs: 1000 for periodic open-position snapshots. That stream does // not include terminal settlement rows. const openPositions = client.websocket.private.positions({ intervalMs: 1000 }); // Private RFQ deliveries for this account (maker acceptances and outcomes) const rfqDeliveries = client.websocket.private.rfqDeliveries({ scope: "account" }); rfqDeliveries.on("message", (delivery) => { console.log(delivery.i, delivery.r, delivery.x, delivery.q); }); await orders.ready; ``` The public RFQ discovery stream is deliberately separate from these private deliveries: subscribe with `client.websocket.public.rfqs()` to discover open auctions, then use `client.websocket.private.rfq.submitQuote()` or `confirmQuote()` to perform authenticated maker actions. See the [RFQ deep dive](/tools/typescript-sdk/deep-dives/rfq#complete-maker-example-with-a-pricing-hook) for the complete flow and application decision hooks. ### Browser limitation Browser `WebSocket` cannot set custom HTTP headers on the upgrade request. `BrowserOAuthAuth` authenticates REST only; it does not authenticate private RIO WebSocket streams or request methods. The browser entry point exposes only `client.websocket.public`, so private operations are neither available in its API nor included in its WebSocket bundle. Browser apps can use: - Public streams (trades, depth, book tickers) — no auth needed - REST endpoints via OAuth for authenticated operations If a first-party Gemini web application has a cookie-authenticated WebSocket endpoint, that is an application-specific integration and is not the SDK's browser authentication mechanism. Use the server entry point or a trusted server-side relay when an SDK integration needs private WebSocket access. ### WebSocket order operations Place and cancel orders over WebSocket for lower latency: ```ts // Place an order const result = await client.websocket.private.placeOrder({ symbol: "BTCUSD", side: "BUY", type: "LIMIT", price: "50000.00", quantity: "0.001", timeInForce: "GTC", }); // response: { id (request correlation ID), status, result? } // The exchange order ID is inside the result object: const orderResult = result.result as Record | undefined; if (!orderResult?.orderId) throw new Error("Place response missing orderId"); // Cancel an order using the exchange order ID await client.websocket.private.cancelOrder({ orderId: String(orderResult.orderId) }); ``` Cancellation methods that affect multiple orders require explicit confirmation: ```ts await client.websocket.private.cancelAllOrders({ confirm: true }); await client.websocket.private.cancelSessionOrders({ confirm: true }); ``` ## Reconnection WebSocket connections reconnect automatically on disconnection with exponential backoff: - Public stream subscriptions are replayed after reconnect - Authenticated streams re-authenticate with fresh credentials (nonces and tokens are regenerated) - **Mutating requests (order placement/cancellation) are never replayed** — they reject with an error if the connection drops mid-request Monitor reconnection: ```ts const stream = client.websocket.public.trades("BTCUSD"); stream.on("resubscribed", () => { console.log("Stream reconnected and resubscribed"); }); stream.on("subscriptionError", (err) => { console.error("Resubscription failed:", err); }); ``` ### Stream state ```ts stream.state; // "active" | "reconnecting" | "failed" | "closed" stream.lastError; // the last error, if any stream.malformedFrameCount; // count of frames that couldn't be parsed ``` ## WebSocket architecture The transport and session implementation is intentionally internal. Applications should use the typed `client.websocket.public` or `client.websocket.private` facade returned by `createClient()`; this keeps connection lifecycle, request correlation, reconnection, and stream cleanup in one place. Custom socket behavior can be supplied with the public `webSocketFactory` option on the server entry point. ## What's next - [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — every stream, method, and wire-format field - [Order Book Reconstruction](/tools/typescript-sdk/deep-dives/order-book) — snapshot + diff internals and gap recovery - [Error handling](/tools/typescript-sdk/errors) — WebSocket-specific errors and connection failures - [Patterns & recipes](/tools/typescript-sdk/patterns) — heartbeat, liveness checks, and advanced configuration --- URL: https://developer.gemini.com/tools/typescript-sdk/quickstart.md # TypeScript SDK — Quickstart Install the SDK, choose an entry point, and make your first request. ## Install ```bash npm install @gemini-markets/sdk ``` The package has two client runtime entry points. Pick the one that matches your runtime: | Entry point | Import from | Use when | | --- | --- | --- | | **Server** | `@gemini-markets/sdk/server` | Node.js 22.4+, Bun, Deno — API keys, HMAC signing, confidential OAuth, REST and public WebSocket; authenticated WebSocket with `ws` or a custom factory | | **Browser** | `@gemini-markets/sdk/browser` | Frontend apps, Cloudflare Workers — public data, OAuth PKCE (no secrets) | The published package does not export the bare `@gemini-markets/sdk` path. Use the explicit `/server` or `/browser` entry point for every import. The server entry re-exports everything from the browser entry, so you never need both imports in one file. Authenticated WebSocket connections require the optional `ws` peer dependency or a custom `webSocketFactory`. Public WebSocket connections use the runtime's native WebSocket implementation when available. To use the built-in authenticated WebSocket factory: ```bash npm install ws ``` ## Server — authenticated client ```ts import { createClient, HmacAuth } from "@gemini-markets/sdk/server"; const client = await createClient({ env: "sandbox", auth: new HmacAuth({ apiKey: process.env.GEMINI_API_KEY!, apiSecret: process.env.GEMINI_API_SECRET!, }), }); // Fetch market data (public — no auth required) const symbols = await client.marketData.listSymbols(); console.log(symbols); // Fetch account balances (authenticated) const balances = await client.account.getAvailableBalances({ account: "primary" }); console.log(balances); client.close(); ``` `createClient()` is **async** on the server. With an authenticated client and no custom `webSocketFactory`, it preloads the optional `ws` package. If you only need REST, skip that overhead: ```ts const client = await createClient({ env: "sandbox", auth: new HmacAuth({ apiKey, apiSecret }), skipWsInit: true, }); ``` ## Browser — public data ```ts import { createClient } from "@gemini-markets/sdk/browser"; const client = createClient({ env: "sandbox" }); const symbols = await client.marketData.listSymbols(); const ticker = await client.marketData.getTicker({ symbol: "BTCUSD" }); client.close(); ``` For authenticated browser REST access (e.g. placing orders on behalf of a user), use OAuth PKCE. Browser OAuth does not authenticate private WebSocket operations. See [Authentication](/tools/typescript-sdk/authentication#browser-oauth-pkce). ## Environments The SDK supports two environments: ```ts // Sandbox (testing) — used in examples throughout these docs const sandboxClient = await createClient({ env: "sandbox", auth }); ``` ```ts // Production (real money) const prodClient = await createClient({ env: "production", auth }); ``` > **Warning:** `env` is required. The SDK throws if you omit it, so always choose `"sandbox"` or `"production"` explicitly. Use `env: "sandbox"` during development and testing. Get sandbox credentials at [exchange.sandbox.gemini.com](https://exchange.sandbox.gemini.com/settings/api). See the [sandbox guide](/get-started/sandbox) for details. ## Service namespaces The client exposes every API surface as a typed namespace: | Namespace | Description | | --- | --- | | `client.predictions` | Prediction markets — events, orders, positions, combos | | `client.marketData` | Symbols, tickers, candles, order books, prices | | `client.trading` | Spot orders, trade history, volume | | `client.margin` | Margin account, rates, order preview | | `client.perpetuals` | Perpetual futures — positions, funding, risk | | `client.account` | Balances, account details, roles, subaccounts, banking, deposit and approved-address management, OAuth revocation | | `client.staking` | Staking balances, rates, rewards, history, stake and unstake | | `client.transfers` | Withdrawals, internal transfers, transfer and transaction history, custody-fee transfers, gas-fee estimates | | `client.clearing` | OTC clearing orders, counterparties, brokers | | `client.instant` | Instant-execution quote requests and order execution | | `client.websocket.public` | Real-time public market streams and RFQ discovery (browser and server) | | `client.websocket.private` (server) | Authenticated order/account streams and state-changing order/RFQ methods | Every method is fully typed. Use your IDE's autocomplete to explore parameters and responses — REST types are generated from the OpenAPI specifications, and WebSocket types are generated from the AsyncAPI specification. ## What's next - [Authentication](/tools/typescript-sdk/authentication) — HMAC, OAuth, and browser PKCE setup - [WebSocket](/tools/typescript-sdk/websocket) — real-time streams and live order books - [Error handling](/tools/typescript-sdk/errors) — error classes, diagnostics, and safe logging - [Patterns & recipes](/tools/typescript-sdk/patterns) — pagination, timeouts, heartbeat, and advanced configuration - [REST API Reference](/tools/typescript-sdk/reference/overview) — every operation across all namespaces - [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — streams, methods, and wire format --- URL: https://developer.gemini.com/tools/typescript-sdk/patterns.md # TypeScript SDK — Patterns & Recipes Common patterns for timeouts, cancellation, pagination, heartbeat, and advanced client configuration. ## Timeouts and cancellation Every SDK method accepts `timeoutMs` and `signal` for deadline control: ```ts // Per-request timeout const symbols = await client.marketData.listSymbols({ timeoutMs: 5_000, }); // Cancellation via AbortController const controller = new AbortController(); const events = client.predictions.listEvents( { status: ["active"] }, { signal: controller.signal }, ); // Cancel from elsewhere controller.abort(); ``` Set a default timeout for all operations on the client: ```ts const client = await createClient({ env: "sandbox", auth, timeoutMs: 10_000, // 10s default for all REST and WebSocket waits }); ``` WebSocket stream listeners also support signal-based cleanup: ```ts const trades = client.websocket.public.trades("BTCUSD"); const controller = new AbortController(); trades.on("message", (trade) => { console.log(trade); }, { signal: controller.signal }); // Later: remove the listener without manually calling off() controller.abort(); ``` ## Pagination ### Auto-paginating async iterators (`for await`) High-level domain namespaces provide typed async generators that automatically handle page boundaries, buffering, and limit clamping: ```ts // Iterate positions across all pages effortlessly for await (const position of client.predictions.iteratePositions({}, { limit: 100 })) { console.log(`Position: ${position.instrumentId} — ${position.totalQuantity}`); } ``` Async iterators support bounds and cancellation: ```ts const controller = new AbortController(); for await (const position of client.predictions.iteratePositions({}, { maxItems: 250, // Stop automatically after 250 items signal: controller.signal, })) { if (shouldStop(position)) { controller.abort(); break; } } ``` ### Manual offset pagination If you need low-level control over page retrieval: ```ts let offset = 0; const positions = []; while (positions.length < 500) { const page = await client.predictions.getPositions({ limit: 100, offset, }); const items = page.positions ?? []; positions.push(...items); if (items.length < 100) break; offset += items.length; } ``` ### Deduplication Offset pagination is not snapshot-consistent — records can shift between pages while you're iterating. Track a stable identity when duplicate records must fail loudly: ```ts const seen = new Set(); for (const position of positions) { const key = `${position.instrumentId}:${position.outcome}`; if (seen.has(key)) throw new Error(`Duplicate position: ${key}`); seen.add(key); } ``` ## Lossless Decimal Math (`decimal`) To prevent IEEE-754 floating-point inaccuracies in financial calculations (e.g. `0.1 + 0.2 !== 0.3`), the SDK includes a built-in, zero-dependency `decimal` utility powered by native `BigInt` scaling: ```ts import { decimal } from "@gemini-markets/sdk/server"; // Exact arithmetic on price/size strings const total = decimal.add("100.10", "200.20"); // "300.3" const spread = decimal.subtract("100.50", "100.25"); // "0.25" const fee = decimal.multiply("1000.00", "0.0015"); // "1.5" const unitPrice = decimal.divide("100", "3", 6); // "33.333333" // Financial rounding & comparisons const rounded = decimal.round("10.556", 2); // "10.56" const isCheaper = decimal.compare("99.99", "100.00"); // -1 const isPositive = decimal.isPositive("-5.00"); // false // Exponential / scientific notation parsing const btcUnits = decimal.normalize("1e-8"); // "0.00000001" ``` ## Explicit Resource Management (`using` / `await using`) The SDK supports TypeScript 5.2+ explicit resource management (`Symbol.dispose` and `Symbol.asyncDispose`) for automatic cleanup of sockets, streams, and order books: ```ts // Automatically closes client and active sockets when leaving scope { await using client = await createClient({ env: "sandbox", auth }); const symbols = await client.marketData.listSymbols(); console.log(symbols); // Scoped WebSocket streaming const stream = client.websocket.public.trades("BTCUSD"); try { for await (const trade of stream) { console.log(trade); break; // stream is automatically unsubscribed & closed on break } } finally { await stream.close(); } } // client.close() is automatic; stream cleanup is explicit ``` ## Heartbeat Keep a session alive with an explicit heartbeat. The heartbeat is stopped by default — you control its lifecycle: ```ts const heartbeat = client.createHeartbeat({ intervalMs: 15_000, // default onError: (err) => console.error("Heartbeat failed:", err), }); heartbeat.start(); // Later heartbeat.stop(); ``` The heartbeat sends a `POST /v1/heartbeat` request at the configured interval. It requires an authenticated client (HMAC or OAuth). ## File responses Some endpoints return binary files (XLSX, CSV) instead of JSON: ```ts const report = await client.marketData.getFundingAmountReportFile({ symbol: "BTCGUSDPERP", }); // report.bytes: Uint8Array — the raw file content // report.contentType: string | undefined (e.g. "text/csv" or XLSX MIME type) // report.contentDisposition: string | undefined (Content-Disposition header) // Write to disk (Node.js) import { writeFileSync } from "node:fs"; const ext = report.contentType?.includes("csv") ? "csv" : "xlsx"; writeFileSync(`funding-report.${ext}`, report.bytes); ``` ## Prediction markets — terms acceptance Prediction market order placement requires accepting the current terms. You can check status proactively for UI, but the order endpoint is authoritative: ```ts // Check terms status const terms = await client.predictions.getPredictionMarketsTermsStatus(); if (!terms.hasAcceptedLatest) { // Show terms to the user, get explicit consent, then: await client.predictions.acceptTerms(); } // Now orders will work await client.predictions.placeOrder({ /* ... */ }); ``` If you skip this, the order endpoint returns `AcceptTermsRequired`. Handle that response by showing the terms, obtaining consent, accepting them, and retrying. ## Advanced client options The full set of options available on `createClient()`: ```ts const client = await createClient({ // Environment env: "sandbox", // "sandbox" | "production" // Authentication auth: new HmacAuth({ apiKey, apiSecret }), // Timeouts and retries timeoutMs: 30_000, // default: 30s maxRetries: 5, // default: 5 (safe reads only) backoff: { baseMs: 500, // initial backoff (default: 500) capMs: 30_000, // maximum backoff factor: 2, // exponential factor }, // WebSocket skipWsInit: false, // true to skip ws preloading (REST-only) webSocketFactory: customFactory, // custom socket factory webSocketLiveness: { intervalMs: 30_000, // liveness check interval timeoutMs: 5_000, // liveness check timeout }, webSocketMaxMessageSizeBytes: 1_048_576, // 1MB frame limit // Custom fetch-compatible transport (for proxies, instrumentation, or testing) fetch: customFetchImpl, // Observability & Telemetry logger: new ConsoleLogger({ minLevel: "debug" }), onDiagnostic: (event) => telemetry.record(event), onRequest: (req) => console.log(`[HTTP] ${req.method} ${req.endpoint} (attempt ${req.attempt})`), onResponse: (res) => console.log(`[HTTP] ${res.status} in ${res.durationMs}ms`), }); ``` ## Runtime compatibility The SDK's REST and authentication layers run on runtimes with Web Crypto and standard `fetch`. WebSocket support additionally requires a native WebSocket; authenticated WebSockets require a socket factory that can set upgrade headers. | Runtime | Browser entry | Server entry | Notes | | --- | --- | --- | --- | | **Node.js** 22.4+ | Yes | Yes | Full support; native WebSocket available | | **Bun** | Yes | Yes | Full lossless integer support; authenticated WebSocket requires `ws` compatibility or a custom factory | | **Deno** | Yes | Yes | Full lossless integer support; authenticated WebSocket requires `ws` compatibility or a custom factory | | **Cloudflare Workers** | Yes | No | No `ws` package — browser entry only; full lossless integer support | | **Browsers** | Yes | No | Public WebSockets only; OAuth PKCE authenticates REST, while private WebSockets require a server or relay | The server entry requires the `ws` peer dependency for authenticated WebSocket connections: ```bash npm install ws ``` `ws` is optional — if you only use REST endpoints with `skipWsInit: true`, you don't need it. ## Closing the client Always close the client when done to release WebSocket connections: ```ts const client = await createClient({ env: "sandbox", auth }); try { // ... use the client } finally { client.close(); } ``` `close()` shuts down all active WebSocket streams and the shared session. It does not cancel in-flight REST requests. ## What's next - [API Reference](/tools/typescript-sdk/reference/overview) — all 106 operations across 10 REST namespaces, with code examples - [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — streams, methods, and wire format - [Deep dives](/tools/typescript-sdk/deep-dives/order-book) — order book reconstruction, RFQ, sessions, transport internals --- URL: https://developer.gemini.com/tools/typescript-sdk/errors.md # TypeScript SDK — Error Handling The SDK uses typed error classes so you can catch specific failure modes. Every SDK error extends `SdkError`. ## Error class hierarchy ``` SdkError Base class for all SDK errors ├── ApiError Any non-2xx REST response │ ├── InvalidRequest 400 — malformed or rejected request │ ├── InvalidNonce 400 — nonce reused or not increasing │ ├── MissingNonce 400 — nonce not present in payload │ ├── InvalidSignature 400 — HMAC signature mismatch │ ├── MissingRole 403 — API key lacks required role │ ├── AcceptTermsRequired 403 — must accept terms before trading │ ├── NotFoundError 404 — resource does not exist │ ├── InsufficientFunds 406 — not enough balance │ ├── RateLimitError 429 — rate limit exceeded │ └── ServiceUnavailable 5xx — exchange down or errored ├── ValidationError Request body fails documented shape check ├── ConnectionError WebSocket open failed or dropped ├── WebSocketRequestError Non-success WebSocket method response ├── RequestTimeoutError Operation exceeded deadline ├── RequestAbortedError Caller cancelled via AbortSignal ├── OAuthStateError OAuth callback state mismatch ├── OAuthAuthorizationError OAuth authorization denied ├── OAuthTokenError OAuth token endpoint failure ├── EndpointMismatch Internal: payload path mismatch └── ResyncRequiredError Order book gap detected ``` ## Catching errors Catch broadly or narrowly depending on your needs: ```ts import { SdkError, ApiError, RateLimitError, AcceptTermsRequired, } from "@gemini-markets/sdk/server"; try { await client.predictions.placeOrder({ /* ... */ }); } catch (err) { if (err instanceof AcceptTermsRequired) { // User must accept terms first await client.predictions.acceptTerms(); // retry... } else if (err instanceof RateLimitError) { // Back off and retry console.log("Rate limited, status:", err.status); } else if (err instanceof ApiError) { // Any other API error console.log(err.status, err.reason, err.code, err.category); } else if (err instanceof SdkError) { // SDK-level error (timeout, connection, validation) console.log(err.message); } } ``` ## Error metadata Every `ApiError` carries structured fields for programmatic handling: ```ts catch (err) { if (err instanceof ApiError) { err.status; // HTTP status code (400, 403, 429, etc.) err.reason; // Server error string ("InvalidNonce", "RateLimit", etc.) err.code; // Stable SDK code ("invalid_request", "rate_limited", etc.) err.category; // Error family ("validation", "authentication", "rate_limit", etc.) err.serverCode; // Raw server error code, if present err.metadata; // Request metadata (endpoint, method, correlation ID, status) } } ``` The `code` and `category` fields are stable across SDK versions — use them for programmatic branching. The `reason` field is the verbatim server string and may change. ## Retries The SDK retries automatically for **generated safe-read operations only** (GET-equivalent endpoints). Retries trigger on: - Network failures - HTTP 429 (rate limit) — respects `Retry-After` header - HTTP 502, 503, 504 (transient server errors) **Mutating operations are never retried.** A failed order placement stays failed — you decide whether to retry. Configure retry behavior: ```ts const client = await createClient({ env: "sandbox", auth, maxRetries: 3, // default: 5 backoff: { baseMs: 500, // default: 500 capMs: 15_000, // default: 30_000 factor: 2, // default: 2 }, }); ``` ## Safe error serialization Use `serializeError()` to log errors safely. It strips raw response bodies and credentials while preserving structure: ```ts import { serializeError } from "@gemini-markets/sdk/server"; try { await client.trading.createNewOrder({ /* ... */ }); } catch (err) { // Safe for logging — no secrets, no raw bodies console.log(JSON.stringify(serializeError(err))); // Include raw body only for debugging (treat as sensitive) console.log(serializeError(err, { includeRawBody: true })); } ``` The serialized output includes: `name`, `message` (redacted), `status`, `reason` (only recognized values), `code`, `category`, `metadata` (endpoint, method, correlation ID), and `operationContext`. ## Diagnostics The SDK emits structured diagnostic events across REST, OAuth, WebSocket, and order-book operations. Diagnostics are silent by default. ### Diagnostic listener Receive every event as a structured object: ```ts const client = await createClient({ env: "sandbox", auth, onDiagnostic: (event) => { // event.level: "debug" | "info" | "warn" | "error" // event.component: "rest" | "oauth" | "websocket" | "order_book" // event.name: specific event name // event.correlationId: stable ID for this request or WebSocket subscription // event.response: safe metadata (endpoint, status, correlation ID) myTelemetry.record(event); }, }); ``` When using `OAuthAuth`, pass the same `onDiagnostic` callback to include token exchange and refresh events: ```ts const auth = new OAuthAuth({ // ... onDiagnostic: (event) => myTelemetry.record(event), }); ``` ### OpenTelemetry (optional) The SDK includes an optional adapter at `@gemini-markets/sdk/opentelemetry`. It maps safe diagnostics to OpenTelemetry client spans for REST and OAuth requests, WebSocket requests and subscriptions, reconnects, and order-book recovery events. The adapter does not configure a provider or exporter, and the core SDK remains dependency-free unless this subpath is imported. Install the OpenTelemetry API alongside the provider and exporter selected by your application: ```bash npm install @opentelemetry/api ``` Pass the tracer from your configured provider to the adapter and connect its diagnostic listener to the client: ```ts import { trace } from "@opentelemetry/api"; import { createOpenTelemetryHooks } from "@gemini-markets/sdk/opentelemetry"; import { createClient, HmacAuth } from "@gemini-markets/sdk/server"; const telemetry = createOpenTelemetryHooks({ tracer: trace.getTracer("my-trading-service"), }); const client = await createClient({ env: "sandbox", auth: new HmacAuth({ apiKey, apiSecret }), onDiagnostic: telemetry.onDiagnostic, }); // End active subscription spans during application shutdown. telemetry.shutdown(); ``` Spans use `SpanKind.CLIENT`, standard HTTP method and response-status attributes where applicable, and Gemini-specific attributes for operation names, correlation IDs, retry counts, exchange request IDs, stream names, and symbols. Request and response bodies, credentials, tokens, signatures, and raw error bodies are not copied into spans. Configure and flush the provider through your application’s normal OpenTelemetry shutdown path. By default, HTTP span names use the method and stable generated operation name when available (for example, `POST trading.createNewOrder`); otherwise they use only the method. Dynamic endpoint values are not used as span names. This adapter provides SDK-level logical spans; use your OpenTelemetry HTTP or `undici` instrumentation for wire-level HTTP context propagation, and avoid enabling two instrumentations for the same network span unless both layers are intentional. ### Console logger For development, use the built-in console logger: ```ts import { ConsoleLogger } from "@gemini-markets/sdk/server"; const client = await createClient({ env: "sandbox", auth, logger: new ConsoleLogger({ minLevel: "debug" }), }); ``` Log levels: `debug`, `info`, `warn`, `error`. Set `minLevel` to control verbosity. ### What's redacted Diagnostic events and serialized errors **never include**: request bodies, response bodies, credentials, signatures, tokens, API keys, or nonces. They **do include**: endpoint paths, HTTP methods, status codes, correlation IDs, exchange request IDs, rate-limit headers, retry counts, and content types. Use `serializeError(err, { includeRawBody: true })` only when you need the raw body for debugging, and treat that output as sensitive. ## What's next - [Patterns & recipes](/tools/typescript-sdk/patterns) — timeouts, cancellation, pagination, and heartbeat - [API Reference](/tools/typescript-sdk/reference/overview) — all operations with their retry and validation behavior - [WebSocket Sessions](/tools/typescript-sdk/deep-dives/websocket-sessions) — reconnection and connection error recovery - [Transport & Signing](/tools/typescript-sdk/deep-dives/transport-and-signing) — retry policy internals --- URL: https://developer.gemini.com/tools/typescript-sdk/authentication.md # TypeScript SDK — Authentication The SDK supports three authentication strategies. Pick the one that matches your application: | Strategy | Import | Use when | | --- | --- | --- | | **HMAC** | `@gemini-markets/sdk/server` | Server-side apps with API key + secret | | **OAuth (confidential)** | `@gemini-markets/sdk/server` | Server-side apps acting on behalf of users | | **OAuth (public/PKCE)** | `@gemini-markets/sdk/browser` | Browser and mobile apps — no client secret | ## HMAC authentication HMAC is the simplest path for server-to-server integration. Every request is signed with your API secret using HMAC-SHA384. ```ts import { createClient, HmacAuth } from "@gemini-markets/sdk/server"; const client = await createClient({ env: "sandbox", auth: new HmacAuth({ apiKey: process.env.GEMINI_API_KEY, apiSecret: process.env.GEMINI_API_SECRET, }), }); const balances = await client.account.getAvailableBalances({ account: "primary" }); client.close(); ``` The SDK handles payload encoding, nonce generation, and signature computation automatically. You never construct headers manually. ### Nonce modes Every authenticated request includes a nonce to prevent replay attacks. The SDK supports two modes: ```ts // Monotonic (default) — strictly increasing counter based on Date.now() new HmacAuth({ apiKey, apiSecret }); // Time-based — Unix epoch seconds. Required by some sandbox configurations. new HmacAuth({ apiKey, apiSecret, nonceMode: "time-based" }); ``` Use `"time-based"` if the sandbox returns `InvalidNonce` errors with the default mode. ### How HMAC signing works For reference, this is what the SDK does on every authenticated request: 1. Build a JSON payload with the `request` path and a `nonce` 2. Base64-encode the payload → `X-GEMINI-PAYLOAD` header 3. HMAC-SHA384 sign the base64 string with your secret → `X-GEMINI-SIGNATURE` header 4. Send the API key in `X-GEMINI-APIKEY` See the [API key authentication docs](/authentication/api-key) for the full protocol specification. ## OAuth — confidential server client Use `OAuthAuth` when your server acts on behalf of a user who has authorized your application. Confidential clients have a `client_secret`. ```ts import { createClient, OAuthAuth } from "@gemini-markets/sdk/server"; const auth = new OAuthAuth({ client: { type: "confidential", clientId: process.env.OAUTH_CLIENT_ID, clientSecret: process.env.OAUTH_CLIENT_SECRET, redirectUri: "https://yourapp.com/callback", }, env: "sandbox", tokenStore: myTokenStore, // you implement this }); ``` ### Authorization flow OAuth requires a multi-step flow: redirect the user, receive a callback, exchange the code for tokens. ```ts // Step 1: Generate the authorization URL const { url, transaction } = await auth.beginAuthorization([ "orders:create", "orders:read", "balances:read", ]); // Step 2: Redirect the user to `url` // They log in at Gemini and authorize your app // Step 3: Handle the callback const tokens = await auth.completeAuthorization(callbackUrl, transaction); // Step 4: Use the authenticated client const client = await createClient({ env: "sandbox", auth }); const positions = await client.predictions.getPositions({ limit: 10 }); ``` ### Implementing a token store The SDK does not persist tokens — you provide a `tokenStore` that handles storage. The store must implement `load`, `save`, `clear`, `consumeAuthorizationState`, and `runExclusive` for shared locking and one-shot authorization-code exchanges: ```ts interface OAuthTokenStore { load(): Promise; save(tokens: OAuthTokens): Promise; clear(): Promise; /** Atomically claim a callback state; return false when it was claimed before. */ consumeAuthorizationState(state: string): Promise; runExclusive(operation: () => Promise): Promise; } ``` The lock must cover every OAuthAuth instance and process sharing the store. Implement it with a distributed lock (e.g., Redis or a database row lock) when the store is shared across processes, so concurrent single-use refresh token rotations and authorization-code exchanges cannot race. Implement `consumeAuthorizationState` as a durable atomic claim with a short expiry (for example, a database insert with a unique state key and a ten-minute TTL) whenever authorization transactions can cross page or process boundaries. The method must return `false` for a state that has already been claimed. A minimal in-memory implementation for development: ```ts class MemoryTokenStore { private tokens?: OAuthTokens; private authorizationStates = new Set(); async load() { return this.tokens; } async save(tokens: OAuthTokens) { this.tokens = tokens; } async clear() { this.tokens = undefined; } async consumeAuthorizationState(state: string) { if (this.authorizationStates.has(state)) return false; this.authorizationStates.add(state); return true; } async runExclusive(operation: () => Promise) { const result = this.lock.then(operation, operation); this.lock = result.then(() => undefined, () => undefined); return result; } private lock = Promise.resolve(); } ``` ### Token refresh Access tokens expire after 24 hours. The SDK refreshes them automatically when `credentialHeaders()` detects an expired token. Refresh happens inside the store's `runExclusive` lock to prevent concurrent rotation of single-use refresh tokens. You can tune refresh timing: ```ts new OAuthAuth({ // ... refreshSkewMs: 60_000, // refresh 60s before expiry (default) }); ``` ### Revocation Revoke tokens explicitly when a user disconnects your app: ```ts await auth.revoke(); // tokens are cleared from the store after successful server-side revocation ``` ## Browser OAuth (PKCE) Browser apps cannot hold a client secret. Use `BrowserOAuthAuth` which enforces public-client PKCE at both the type and runtime levels — you cannot accidentally pass a confidential client. ```ts // login.ts — the page that starts the OAuth flow import { createClient, BrowserOAuthAuth } from "@gemini-markets/sdk/browser"; const auth = new BrowserOAuthAuth({ client: { type: "public", clientId: "your-client-id", redirectUri: "http://localhost:3000/callback", }, env: "sandbox", tokenStore: myBrowserTokenStore, }); // Step 1: Start authorization (generates PKCE challenge automatically) const { url, transaction } = await auth.beginAuthorization(["orders:read"]); // Step 2: Persist the transaction — the redirect will lose in-memory state sessionStorage.setItem("gemini_oauth_tx", JSON.stringify(transaction)); // Step 3: Redirect the user window.location.href = url; ``` ```ts // callback.ts — the page Gemini redirects back to const raw = sessionStorage.getItem("gemini_oauth_tx"); if (!raw) throw new Error("OAuth transaction not found — was the flow started?"); const transaction = JSON.parse(raw); sessionStorage.removeItem("gemini_oauth_tx"); // Step 4: Complete the exchange with the saved transaction await auth.completeAuthorization(window.location.href, transaction); // Step 5: Use the client const client = createClient({ env: "sandbox", auth }); const events = await client.predictions.listEvents({ status: ["active"] }); ``` The PKCE code challenge and verifier are generated automatically using Web Crypto (`crypto.subtle`). The verifier is sent during code exchange — no secret ever leaves the browser. Browser OAuth authenticates REST requests only. It does not make private WebSocket streams or WebSocket order methods available in the browser: native browser WebSockets cannot send the required upgrade `Authorization` header, and the SDK rejects private WebSocket operations from the browser entry point. Use `@gemini-markets/sdk/server` or a server-side relay for authenticated WebSockets. ### Scopes Scopes control what the OAuth token can access. Request only what your app needs: | Scope | Grants | | --- | --- | | `orders:create` | Place and cancel orders | | `orders:read` | View orders and trade history | | `balances:read` | View account balances | | `addresses:read` | View approved withdrawal addresses | | `history:read` | View transaction history | The full scope list is in the [OAuth documentation](/authentication/oauth#oauth-scopes). ### Browser token persistence In the browser, persist tokens to `localStorage` or `sessionStorage`: ```ts const stateKey = "gemini_oauth_used_states"; const browserTokenStore = { async load() { const raw = localStorage.getItem("gemini_tokens"); return raw ? JSON.parse(raw) : undefined; }, async save(tokens) { localStorage.setItem("gemini_tokens", JSON.stringify(tokens)); }, async clear() { localStorage.removeItem("gemini_tokens"); }, async consumeAuthorizationState(state: string) { const now = Date.now(); const raw = localStorage.getItem(stateKey); const states = raw ? JSON.parse(raw) as Record : {}; for (const [value, expiresAt] of Object.entries(states)) { if (expiresAt <= now) delete states[value]; } if (states[state] !== undefined) return false; states[state] = now + 10 * 60_000; localStorage.setItem(stateKey, JSON.stringify(states)); return true; }, async runExclusive(operation: () => Promise): Promise { if (!("locks" in navigator)) { throw new Error("The Web Locks API is required for cross-tab OAuth token refresh locking"); } return (navigator as Navigator & { locks: LockManager }).locks.request( "gemini-oauth-token-refresh", { mode: "exclusive" }, operation, ); }, }; ``` `navigator.locks` provides an origin-wide exclusive lock, so multiple tabs do not rotate the same single-use refresh token or exchange the same authorization code concurrently. The state claim is short-lived and is only for replay protection; persist the authorization transaction itself (including the PKCE verifier) in `sessionStorage` as shown above. If the target browser does not support the Web Locks API, use a compatible Web Locks polyfill or move token refresh into a service with a shared lock. For production apps, consider encrypting tokens at rest and using `sessionStorage` for shorter-lived sessions. ## What's next - [WebSocket](/tools/typescript-sdk/websocket) — real-time streams with authenticated access - [Error handling](/tools/typescript-sdk/errors) — OAuth-specific error types and recovery --- URL: https://developer.gemini.com/rest-api/prediction-markets/volume.md # Volume import { IconChartLine } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/rest-api/prediction-markets/terms.md # Terms import { IconCheckCircleOutlined, IconDocumentOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/rest-api/prediction-markets/rewards.md # Rewards import { IconChartLine } from "@hubble/icons/dist/cjs/web/index.js"; Endpoints for the Maker Rebate and Liquidity Rewards programs. Maker Rebate pays per-fill rebates on resting limit orders that get filled. Liquidity Rewards distributes daily USD pools across qualifying makers based on quote uptime, spread, and size. Response fields on these endpoints use snake_case.} links={[ { icon: IconChartLine, title: "Get Maker Rebate Rates", href: "/rest-api/prediction-markets/rewards/get-maker-rebate-rates" }, { icon: IconChartLine, title: "List Maker Rebate Payouts", href: "/rest-api/prediction-markets/rewards/list-maker-rebate-payouts" }, { icon: IconChartLine, title: "Get Maker Rebate Lifetime Summary", href: "/rest-api/prediction-markets/rewards/get-maker-rebate-lifetime-summary" }, { icon: IconChartLine, title: "Get Liquidity Rewards Config", href: "/rest-api/prediction-markets/rewards/get-liquidity-rewards-config" }, { icon: IconChartLine, title: "List Liquidity Rewards Events", href: "/rest-api/prediction-markets/rewards/list-liquidity-rewards-events" }, { icon: IconChartLine, title: "Get Liquidity Rewards Daily Summary", href: "/rest-api/prediction-markets/rewards/get-liquidity-rewards-daily-summary" }, { icon: IconChartLine, title: "Get Liquidity Rewards Lifetime Summary", href: "/rest-api/prediction-markets/rewards/get-liquidity-rewards-lifetime-summary" }, ]} prev={{ label: "Positions", href: "/rest-api/prediction-markets/positions" }} next={{ label: "Get Maker Rebate Rates", href: "/rest-api/prediction-markets/rewards/get-maker-rebate-rates" }} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/positions.md # Positions import { IconDocumentOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/rest-api/prediction-markets/order-management.md # Order Management import { IconClearOutlined, IconDocumentOutlined, IconPlusCircleOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/rest-api/prediction-markets/events.md # Events import { IconChartLine, IconMenu } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/rest-api/prediction-markets/combos.md # Combos import { IconChartLine, IconPlusCircleOutlined } from "@hubble/icons/dist/cjs/web/index.js"; --- URL: https://developer.gemini.com/rest-api/common/oauth.md # OAuth import { IconKeyOutlined } from "@hubble/icons/web"; --- URL: https://developer.gemini.com/rest-api/common/admin.md # Admin import { IconPlusCircleOutlined, IconInfoOutlined, IconMenu, IconEditOutlined, IconCheckCircleOutlined } from "@hubble/icons/web"; --- URL: https://developer.gemini.com/prediction-markets/websocket/streams.md # Stream Reference Prediction Markets uses the core book, depth, trade, order, balance, and position stream protocol. Prediction Markets WebSocket streams follow the standard Gemini WebSocket protocol. Symbols use the prediction markets format (e.g. `GEMI-BTC05M2606011000-UP`). Replace example symbols with active `instrumentSymbol` values from `GET /v1/prediction-markets/events`. Use WebSocket streams for active trading and market making. Use REST endpoints for event discovery and state reconciliation. ## Stream Matrix | Stream | Auth | Use for | Start here | |----|----|----|----| | [`{symbol}@bookTicker`](#book-ticker) | Public | Best bid/ask prices and quantities | First price watcher | | [`{symbol}@depth5`, `{symbol}@depth10`, `{symbol}@depth20`](#l2-partial-depth-streams) | Public | Periodic top-of-book snapshots | Simple dashboards | | [`{symbol}@depth`, `{symbol}@depth@100ms`](#l2-differential-depth-streams) | Public | Maintaining a local order book | Market making | | [`{symbol}@trade`](#trade-stream) | Public | Recent executions | Trade tape and analytics | | [`{agency}:{index}@indexPrice`](#index-keyed-price-streams) | Public | Price reference feeds for prediction-market contracts | Contract pricing and settlement | | [`orders@account`](#order-events) | Authenticated | Order lifecycle events | Order state tracking | | [`balances@account`](#balance-updates) | Authenticated | Balance changes | Risk checks | | [`positions@account`](#position-updates) | Authenticated | Low-latency position and settlement deltas | Exposure and settlement tracking | | [`contractStatus`](#contract-status) | Public | Contract status and strike updates | Lifecycle monitors | :::note Authenticated streams require Gemini authentication headers (HMAC or OAuth bearer token) during the WebSocket handshake. Browser WebSocket clients cannot set custom headers; use backend services or proxy servers for account streams. ::: ## Index-Keyed Price Streams Index-keyed price streams provide the live reference price associated with a prediction-market contract's `sourceDetails.agency` and `sourceDetails.index`. Subscribe using the exact values returned by the contract or event discovery API: ```text {agency}:{index}@indexPrice ``` For example: ```text lukka:LS-LRRR-ZECUSD@indexPrice lukka:LS-LRRR-HYPE22USD@indexPrice ``` These streams are public and event-driven. The server resolves the `agency`/`index` pair to the corresponding price feed and delivers the reference-price update using the standard third-party price message format. The matching is case-insensitive, but clients should use the values exactly as returned by the source-details fields. Unknown pairs are rejected as invalid stream names. ## Book Ticker | Schema | Frequency | Description | |----|----|----| | `{symbol}@bookTicker` | Real-time | Real time updates to the best bid/ask price for an order book. | ```json { "u": 1751505576085, "E": 1751508438600117161, "s": "GEMI-BTC05M2606011000-UP", "b": "0.48", "B": "5000", "a": "0.52", "A": "3200" } ``` | Field | Type | Description | |-------|--------|--------------------------| | `u` | number | Update ID | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `b` | string | Best bid price | | `B` | string | Best bid quantity | | `a` | string | Best ask price | | `A` | string | Best ask quantity | | `c` | string | Last trade price (present once the book has traded; omitted otherwise) | | `C` | string | Last trade size (present once the book has traded; omitted otherwise) | --- ## L2 Partial Depth Streams | Schema | Frequency | Description | |----|----|----| | `{symbol}@depth5` | Periodic (1s) | Periodic snapshot of the top 5 levels once per second | | `{symbol}@depth10` | Periodic (1s) | Top 10 levels | | `{symbol}@depth20` | Periodic (1s) | Top 20 levels | | `{symbol}@depth5@100ms` | Periodic (100ms) | Top 5 levels every 100 milliseconds | | `{symbol}@depth10@100ms` | Periodic (100ms) | Top 10 levels | | `{symbol}@depth20@100ms` | Periodic (100ms) | Top 20 levels | ```json { "lastUpdateId": 12345678, "bids": [ ["0.26", "5000"], ["0.25", "2000"] ], "asks": [ ["0.28", "3200"], ["0.29", "1500"] ] } ``` | Field | Type | Description | |----------------|----------|------------------------------| | `lastUpdateId` | number | Last update ID | | `bids` | array | Array of [price, quantity] | | `asks` | array | Array of [price, quantity] | :::note Use a depth snapshot as the starting point for any local order book or dollar calculation, then apply differential depth updates to keep it current. Each level is `[price, quantity]`. Public depth for event contracts is normalized in YES space: for YES notional, compute `yesPrice * quantity`; for NO notional, compute `(1 - yesPrice) * quantity`. ::: --- ## L2 Differential Depth Streams | Schema | Frequency | Description | |----|----|----| | `{symbol}@depth` | Periodic (1s) | List of all changed price levels in the last second | | `{symbol}@depth@100ms` | Periodic (100ms) | In the last 100 milliseconds | :::tip[Initial Snapshot] Use the [`snapshot` connection parameter](/prediction-markets/websocket/introduction#snapshot-parameter) to receive an initial orderbook snapshot when subscribing. Connect with `wss://ws.gemini.com?snapshot=-1` for a full snapshot, or specify a positive number for top N levels. ::: :::note Quantity zero indicates price level removal. ::: There is no separate snapshot message and no `lastUpdateId` field on this stream. When you subscribe with the `snapshot` parameter set to a non-zero value, the first `depthUpdate` frame you receive *is* the snapshot — it carries the full requested book state in its `b`/`a` arrays instead of a delta. Its `U` and `u` identify the first and last update IDs covered by the snapshot; use `u` as the last applied update ID. Every frame after that first one is a normal incremental delta: apply it directly to your local book. If a later frame's `U` skips ahead of the last applied `u`, discard the local book and resubscribe to resync. Without the `snapshot` parameter, or with it set to `0`, every frame you receive (including the first) is an incremental delta. ```json { "e": "depthUpdate", "E": 1751508260659505382, "s": "GEMI-BTC05M2606011000-UP", "U": 12345677, "u": 12345678, "b": [ ["0.48", "5000"], ["0.47", "0.00"] ], "a": [ ["0.52", "3200"] ] } ``` | Field | Type | Description | |-------|--------|------------------------------------| | `e` | string | Event type ("depthUpdate") | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `U` | number | First update ID in this event | | `u` | number | Last update ID in this event | | `b` | array | Bid updates [price, quantity] | | `a` | array | Ask updates [price, quantity] | --- ## Trade Stream | Schema | Frequency | Description | |----|----|----| | `{symbol}@trade` | Real-time | Real time trade executions | ```json { "E": 1759873803503023900, "s": "GEMI-BTC05M2606011000-UP", "t": 2840140956529623, "p": "0.50", "q": "10", "m": true } ``` | Field | Type | Description | |-------|---------|--------------------------| | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `t` | number | Trade ID | | `p` | string | Price | | `q` | string | Quantity | | `m` | boolean | Is buyer the maker | --- ## Order Events :::warning Requires an authenticated session ::: | Schema | Frequency | Description | |----|----|----| | `orders@account` | Real-time | Real time order activity for the account associated with the authenticated API key | | `orders@session` | Real-time | Real time order activity for the authenticated API key | Order Event - New: ```json { "e": "orderUpdate", "E": 1759291847686856569, "s": "GEMI-BTC05M2606011000-UP", "i": 73797746498585286, "c": "btc-5m-quote-001", "S": "BUY", "o": "LIMIT", "X": "NEW", "O": "YES", "p": "0.48000", "q": "10", "z": "10", "T": 1759291847686856569 } ``` Order Event - Canceled: ```json { "e": "orderUpdate", "E": 1759291847731455006, "s": "GEMI-BTC05M2606011000-UP", "i": 73797746498585286, "c": "btc-5m-quote-001", "X": "CANCELED", "T": 1759291847731455006 } ``` | Field | Type | Description | |-------|---------|-------------------------| | `e` | string | Event type (`orderUpdate`) | | `E` | number | Event time (nanoseconds) | | `s` | string | Symbol | | `i` | number | Order ID | | `c` | string | Client order ID. For RFQ maker fills, this is the `clientId` supplied to `rfq.submit_quote`, or Gemini's deterministic RFQ client order ID when omitted. | | `S` | string | Side, `BUY / SELL` | | `o` | string | Type, `LIMIT / MARKET / STOP_LIMIT / STOP_MARKET` | | `X` | string | Status, `NEW / OPEN / FILLED / PARTIALLY_FILLED / CANCELED / REJECTED / MODIFIED` | | `O` | string | Event outcome, `YES / NO` | | `p` | string | Order price | | `P` | string | Stop price (`0` when not a stop order) | | `q` | string | Original quantity | | `z` | string | Remaining quantity | | `Z` | string | Executed quantity. For `FILLED` / `PARTIALLY_FILLED` events, this is the quantity filled in the last execution. For `CANCELED` and other events, this is the cumulative quantity filled over the lifetime of the order. Use `Z` (not the order status) to determine how much filled — e.g. a fully-filled `IOC` terminates as `CANCELED`. | | `L` | string | Last execution price | | `t` | number | Trade ID | | `n` | string | Fee amount (only present in 'FILLED' events) | | `m` | boolean | Maker flag on fills: `true` = maker, `false` = taker (present on fills only) | | `r` | string | Rejection reason | | `T` | number | Update time (nanoseconds) | :::note Fields with empty or zero values may be omitted from the event. ::: :::note Post-only and immediate time-in-force orders are **accepted, then cancelled** — they are never `REJECTED`: - `MOC` (maker-or-cancel / post-only): if it would take liquidity, the order is cancelled with `MakerOrCancelWouldTake` and never fills. - `IOC` (immediate-or-cancel): fills whatever crosses immediately, then cancels the remainder with `ImmediateOrCancelWouldPost`. A fully-filled `IOC` still ends with a `CANCELED` event — **so determine what filled from the executed quantity (`Z`), never from the final order status.** - `FOK` (fill-or-kill): fills completely and immediately, or is cancelled in full with `FillOrKillWouldNotFill` (no partial fills). `order.place` for these still returns a `200` response with an initial `NEW`; a true rejection returns a non-`200` status with an error code. ::: #### Rejection Reasons When an order is `REJECTED`, the `r` field contains one of: | Reason | Description | |--------|-------------| | `MarketNotOpen` | Market is closed or paused | | `InsufficientFunds` | Account lacks sufficient balance | | `InvalidPrice` | Price must be between $0.01–$0.99 | | `LimitPriceOffTick` | Price does not align with tick size | | `InvalidQuantity` | Quantity below minimum or off increment | | `InvalidTotalSpend` | Total spend calculation error | | `DuplicateOrder` | Duplicate client order ID | | `InsufficientLiquidity` | Not enough liquidity at price | | `UnknownInstrument` | Trading pair does not exist | | `TERMS_NOT_ACCEPTED` | Latest Prediction Markets terms not accepted. Use the REST terms endpoints to read, check, and accept terms before retrying. | #### Cancellation Reasons When an order is `CANCELED` by the system, the `r` field contains one of: | Reason | Description | |--------|-------------| | `SelfCrossPrevented` | Self-trade prevention triggered | | `FillOrKillWouldNotFill` | FOK order could not fill completely | | `ImmediateOrCancelWouldPost` | IOC order would post to book | | `MakerOrCancelWouldTake` | MOC order would take liquidity | | `AuctionCancelled` | Auction-related cancellation | | `ExceedsPriceLimits` | Price moved beyond limits | --- ## Balance Updates :::warning Requires an authenticated session ::: | Schema | Frequency | Description | |----|----|----| | `balances@account` | Real-time | Real time balance updates for the account associated with the authenticated API key | | `balances@account@1s` | Periodic (1s) | Periodic snapshot of all balances every second for the account associated with the authenticated API key | The `balances@account` stream pushes updates in real time whenever a balance change occurs, and only includes the assets that changed. The `balances@account@1s` stream sends a complete snapshot of all account balances every second, regardless of whether they changed. On subscribe, `balances@account@1s` will immediately send the current balances if available. Balance Update: ```json { "e": "balanceUpdate", "E": 1768250434780000000, "u": 1768250421600000000, "B": [ { "a": "USD", "f": "207.39", "c": "207.39" } ] } ``` | Field | Type | Description | |-------|---------|-------------------------| | `e` | string | Event type ("balanceUpdate") | | `E` | number | Event time (nanoseconds) | | `u` | number | Time of the last account update (nanoseconds) | | `B` | array | Balance updates | | `a` | string | Asset code | | `f` | string | Available balance (amount available to trade) | | `c` | string | Confirmed balance (total balance including pending) | --- ## Position Updates :::warning Requires an authenticated session ::: | Schema | Frequency | Description | |----|----|----| | `positions@account` | Real-time | Real time event-contract position updates for the account associated with the authenticated API key | | `positions@account@1s` | Periodic (1s) | Periodic snapshot of all open event-contract positions every second for the account associated with the authenticated API key | The `positions@account` stream pushes deltas in real time on fill, position open/close, and event-contract settlement events, and only includes the rows that changed. A settlement close is delivered as a terminal row with `position` set to `"0"`, followed by a `settlement_payout` amount carrying the payout currency and outcome. The `positions@account@1s` stream sends a complete snapshot of all open positions every second, regardless of whether they changed. On subscribe, `positions@account@1s` will immediately send the current positions if available. Use `positions@account` for low-latency event-contract position deltas on the same WebSocket connection you use for trading. Reconcile with `POST /v1/prediction-markets/positions` after reconnects, missed messages, and settlement windows. :::note Connect to `wss://ws.gemini.com`. Authentication must be provided on the WebSocket upgrade — you cannot authenticate after the connection is established. Use either HMAC-signed API key headers (account-scoped only; master/group keys are rejected with HTTP 401) or an OAuth 2.0 bearer token (`Authorization: Bearer `). See [Authentication](/prediction-markets/websocket/authentication). ::: Subscribe after the connection opens: ```json { "id": "1", "method": "SUBSCRIBE", "params": ["positions@account"] } ``` Server acknowledgement: ```json { "id": "1", "status": 200 } ``` Snapshot and delta frames share the same shape: ```json { "e": "positionReport", "E": 1760000000000000000, "u": 1759999999000000000, "A": 12345, "P": [ { "t": "ec", "s": "GEMI-BTC05M2606011000-UP", "a": [ { "t": "position", "v": "2.5" } ] } ] } ``` | Field | Type | Description | |-------|------|-------------| | `e` | string | Event type, always `positionReport` | | `E` | number | Event timestamp in nanoseconds | | `u` | number | Last account update timestamp in nanoseconds | | `A` | number | Account ID | | `P` | array | Position rows for this account; an empty array means no open position rows are included | | `P[].t` | string | Product type. Event contracts use `ec` | | `P[].s` | string | Instrument symbol | | `P[].a` | array | Named amount array | | `P[].a[].t` | string | Amount label. Position quantity uses `position`; a settlement close also includes `settlement_payout` | | `P[].a[].v` | string | Decimal string amount. `position` is signed; `settlement_payout` is the payout amount | | `P[].a[].c` | string | Optional asset code. Settlement payouts use `usd`; position quantities omit this field | | `P[].a[].o` | string | Settlement outcome on `settlement_payout`: `YES`, `NO`, or `UNSPECIFIED` | :::note The first subscription for an account returns a snapshot of currently open positions. Subsequent frames are deltas carrying only rows that changed. A position close emits a row with `position` value `"0"` before that row is evicted; a settlement close includes `settlement_payout` and outcome in that same terminal row. Later snapshots omit zero-position rows. The `a` array is intentionally extensible; clients should ignore unknown amount labels instead of failing. ::: --- ## Settlements :::warning Requires an authenticated session ::: There is no standalone `settlements@account` stream. Settlement details are delivered in the terminal `positionReport` delta on `positions@account`. The terminal row contains a zero `position` amount and a `settlement_payout` amount. The payout amount uses `c: "usd"`; its `o` value is `YES`, `NO`, or `UNSPECIFIED`. Subscribe using the standard [Position Updates](#position-updates) request format. Use `POST /v1/prediction-markets/positions/settled` for historical settled positions and reconciliation after reconnects or missed messages. --- ## Contract Status Prediction-market contract lifecycle events — status transitions (e.g. `Awaiting Approval` → `Approved` → `Active`) and strike-populated moments for Up/Down contracts. | Schema | Frequency | Description | |----|----|----| | `contractStatus` | Real-time | Status changes and strike-price updates for prediction-market contracts | ```json # Strike-based contract (e.g. HI78999D63) { "e": "contractStatus", "E": 1776871540195, "s": "gemi-btc15m2604221545-hi78999d63", "k": "btc15m2604221545", "c": "HI78999D63", "i": 134794, "p": "78999.63", "o": "Awaiting Approval", "n": "Approved" } # Up/Down contract (no numeric strike — `p` omitted until populated) { "e": "contractStatus", "E": 1776871295498, "s": "gemi-btc05m2604221630-up", "k": "btc05m2604221630", "c": "UP", "i": 134791, "o": "Awaiting Approval", "n": "Approved" } ``` | Field | Type | Description | |-------|--------|-------------| | `e` | string | Event type (`contractStatus`) | | `E` | number | Event time (Unix milliseconds) | | `s` | string | Instrument symbol | | `k` | string | Event ticker | | `c` | string | Contract ticker (e.g. `HI78999D63`, `UP`, `DOWN`) | | `i` | number | Contract ID | | `p` | string | Strike price parsed from the contract ticker. Omitted for Up/Down contracts until the strike is set at activation | | `o` | string | Previous status | | `n` | string | New status | :::note For Up/Down contracts, `p` is omitted while the strike is unknown and included once it is set — subscribers can detect strike availability by the field's presence. ::: --- URL: https://developer.gemini.com/prediction-markets/websocket/message-format.md # Message Format Our WebSocket API uses JSON-formatted messages for all communication. ### Request Format All requests follow a consistent structure: ```json { "id": "1", "method": "METHOD_NAME", "params": {...} } ``` | Field | Type | Required | Description | |----------|-------------------|----------|------------------------------------------------| | `id` | string \| number | Yes | Unique identifier for matching request/response | | `method` | string | Yes | The method to invoke | | `params` | object \| array | No | Method parameters (varies by method) | ### Response Format Successful responses include the request ID and result: ```json { "id": "1", "status": 200, "result": {...} } ``` | Field | Type | Description | |----------|------------------|------------------------------------------| | `id` | string \| number | Matches the request ID | | `status` | number | HTTP status code | | `result` | any | Method-specific response data | ### Error Response Error responses include error details: ```json { "id": "1", "status": 401, "error": { "code": -1002, "msg": "Authentication required" } } ``` | Field | Type | Description | |-----------------|------------------|---------------------------------| | `id` | string \| number | Matches the request ID | | `status` | number | HTTP status code | | `error.code` | number | Internal error code | | `error.msg` | string | Human-readable error message | ### Error Codes | Code | HTTP Status | Description | |--------|-------------|-------------------------------| | -1000 | 500 | Internal server error | | -1002 | 401 | Authentication required | | -1003 | 429 | Rate limit exceeded | | -1013 | 400 | Invalid parameters | | -1020 | 400 | Unsupported operation | | -2010 | 400 | Order rejected | ### Event Types Streaming events carry an `e` field that identifies the event type, so a single connection can demultiplex every subscription: | `e` value | Stream | |-----------|--------| | `depthUpdate` | L2 differential depth (`{symbol}@depth`, `{symbol}@depth@100ms`) | | `orderUpdate` | Order events (`orders@account`, `orders@session`) | | `balanceUpdate` | Balance updates (`balances@account`, `balances@account@1s`) | | `positionReport` | Position updates (`positions@account`, `positions@account@1s`) | | `contractStatus` | Contract status (`contractStatus`) | The Book Ticker (`{symbol}@bookTicker`), L2 Partial Depth (`{symbol}@depth5` / `@depth10` / `@depth20`), and Trade (`{symbol}@trade`) payloads do **not** carry an `e` field — identify those by the stream you subscribed to. :::note New event types may be added over time. Treat any `e` value you do not recognize as a forward-compatible addition and ignore it. ::: --- URL: https://developer.gemini.com/prediction-markets/websocket/introduction.md # Introduction **Version:** 0.10.7 • **Status:** Production • **Public URL:** `wss://ws.gemini.com` Our WebSocket API provides low latency access to real-time market data and order execution for professional traders and institutions. Built from the ground up for performance, our WebSocket API delivers fastest latency on AWS with enterprise-grade reliability. :::tip [**Try It Now** with our interactive documentation](/prediction-markets/websocket/playground#method-subscribe) ::: ### Key Features - **Low Latency** - Sub-10ms market data updates for competitive advantage - **Real-Time Trading** - Place and cancel orders via WebSocket - **Multiple Streams** - Subscribe to multiple markets simultaneously ### Performance Tiers | Tier | Target | Description | |------|---------------------|-------------| | **Tier 2** _(Public Internet)_ | p99~15ms | Public offering connecting to **AWS us-east-1 over the public internet**. Provides good **baseline performance** with minimal setup complexity. | | **Tier 1** _(In Region)_ | p99~10ms | **Direct connection** to us-east-1 feed. Provides **improved performance** a step above the public offering but requires onboarding to peer to our infrastructure. | | **Tier 0** _(Local Zone)_ | p99~5ms | **Best performance** outside of NY5, physically closest to our data center. Requires onboarding similar to us-east-1. | :::info Please email api@gemini.com to onboard to our WebSocket high performance tiers. ::: ### Connection Parameters Connection-level query parameters can be passed in the WebSocket URL to customize behavior: | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | `snapshot` | integer | `0` | Controls initial orderbook snapshot delivery when subscribing to differential depth streams | | `cancelOnDisconnect` | boolean | `false` | Automatically cancel all open orders when the WebSocket session disconnects | #### Snapshot Parameter The `snapshot` parameter controls whether an initial orderbook snapshot is sent when subscribing to [differential depth streams](/prediction-markets/websocket/streams#l2-differential-depth-streams) (`{symbol}@depth`, `{symbol}@depth@100ms`). **Connection URL example:** ``` wss://ws.gemini.com?snapshot=-1 ``` | `snapshot` Value | Behavior | |------------------|----------| | Not specified / `0` | No snapshot — only incremental updates (default) | | `-1` (or any negative) | Full orderbook snapshot sent immediately on subscribe | | Positive integer (e.g., `100`) | Top N levels snapshot sent on subscribe | :::tip Use `snapshot=-1` to receive a full orderbook snapshot immediately when subscribing to differential depth streams. This is useful for initializing your local orderbook state without needing a separate REST API call. ::: #### Cancel On Disconnect Parameter The `cancelOnDisconnect` parameter enables automatic cancellation of all your open orders when the WebSocket session disconnects. This is a safety feature that helps prevent unwanted exposure from stale orders if your connection drops unexpectedly. **Connection URL example:** ``` wss://ws.gemini.com?cancelOnDisconnect=true ``` | `cancelOnDisconnect` Value | Behavior | |----------------------------|----------| | Not specified / `false` | Orders remain active after disconnect (default) | | `true` | All open orders are automatically cancelled on disconnect | :::warning When `cancelOnDisconnect=true` is enabled, **all** open orders placed via the WebSocket session will be cancelled upon disconnection, including orders that may have been intentionally left open. Ensure your trading strategy accounts for this behavior. ::: :::tip Combine multiple connection parameters using `&`: `wss://ws.gemini.com?snapshot=-1&cancelOnDisconnect=true` ::: --- URL: https://developer.gemini.com/prediction-markets/websocket/authentication.md # Authentication Prediction Markets uses the core WebSocket authentication protocol. Product access and terms acceptance are separate prerequisites. ## Generate an API Key API keys for our WebSocket API have special requirements: 1. Navigate to [API Settings](https://exchange.gemini.com/settings/api) 2. Click **"Create API key"** 3. **Scope:** Select the account you want to trade with 4. **Settings:** - Enable **"Uses a time-based nonce"** - Select **Trader** for trading methods such as `order.place`; **Auditor** or **Trader** is sufficient for `positions@account` 5. Save your API key and secret securely :::warning Only **account-scoped** keys with **time-based** nonces are accepted. Account keys use the `account-...` prefix. Master or group keys, such as `master-...` keys, are rejected with HTTP 401. ::: --- ## Create an Authenticated Connection Connect to `wss://ws.gemini.com` and pass the following headers when establishing the WebSocket connection: | Header | Value | |--------|-------| | `X-GEMINI-APIKEY` | Your account-scoped Gemini API key | | `X-GEMINI-NONCE` | Decimal nonce | | `X-GEMINI-PAYLOAD` | `base64(string(nonce))` | | `X-GEMINI-SIGNATURE` | `hex(hmac_sha384(payload, api_secret))` | :::note Auditor or Trader role is sufficient for `positions@account`. Trader role is required for trading operations such as `order.place`. ::: :::note Before sending trading orders, check `GET /v1/prediction-markets/terms/status`. If `hasAcceptedLatest` is `false`, display `GET /v1/prediction-markets/terms` and accept with `POST /v1/prediction-markets/terms/accept`, then retry the order. ::: :::warning Authentication headers must be provided during the initial WebSocket handshake. You cannot authenticate after the connection is established. ::: ### Signature Generation Step-by-Step ```text # Create a monotonically increasing integer nonce. # Unix seconds or Unix milliseconds are both accepted, but the value must # increase across connections for the same key. nonce = current_unix_timestamp_in_seconds_or_milliseconds # Base64 encode the string form of the nonce. payload = base64_encode(string(nonce)) # Generate a signature using the hmac_sha384 algorithm. signature = hmac_sha384(payload, api_secret) # Convert the signature to hex so it can be passed in the headers. hexSignature = hex(signature) ``` --- ## Alternative: OAuth 2.0 Bearer Token If your application uses [OAuth 2.0](/authentication/oauth) to access the Gemini API, you can authenticate the WebSocket connection with the same access token instead of provisioning an API key. Pass the access token in the `Authorization` header on the WebSocket upgrade request: | Header | Value | |-----------------|--------------------------| | `Authorization` | `Bearer ` | When using OAuth, you do **not** send the `X-GEMINI-APIKEY`, `X-GEMINI-NONCE`, `X-GEMINI-PAYLOAD`, or `X-GEMINI-SIGNATURE` headers. :::note The access token must include a position scope for `positions@account`: `positions:read` or `predictions:positions:read`. See [OAuth scopes](/authentication/oauth#oauth-scopes). ::: :::warning Access tokens are short-lived (default 24 hours). If the token expires during a session, the server will close the connection and you must reconnect with a refreshed token — tokens cannot be rotated on a live connection. See [Using Refresh Tokens](/authentication/oauth#using-refresh-tokens). ::: --- URL: https://developer.gemini.com/prediction-markets/combos-rfq/websocket-streams.md # WebSocket Streams Combo RFQ streams extend the core WebSocket protocol for Prediction Markets. :::caution The combo RFQ WebSocket streams documented on this page are available in production for beta testing. Quoting requires an eligible account with the capabilities described in [Quote Methods](/prediction-markets/combos-rfq/quote-methods). ::: Combo RFQ uses three streams: | Stream | Auth | Use for | |---|---|---| | `requestForQuote` | Public | Discovering RFQ auctions (anonymous broadcast). | | `requestForQuote@account` | Authenticated (`view_orders`) | Your private deliveries: the selected quote after your RFQ closes, acceptances of your quotes, and confirm/decline outcomes. Each delivery has a durable event ID for deduplication. | | `requestForQuote@session` | Authenticated (`view_orders`) | Same account-scoped deliveries as `@account`; an alternate subscription form. Mutually exclusive with `@account`. | ## `requestForQuote` Anonymous, real-time feed of combo request-for-quote auctions. When a taker opens an RFQ, this feed broadcasts the auction so makers can choose to quote it. Only the auction's existence and parameters are broadcast: the requester's identity, the maker accounts, and the individual quotes are never included. After close, only the selected quote is delivered privately to the requester over an authenticated stream. **Auth:** public. Anyone can subscribe. ### Message shape ```json { "e": "requestForQuote", "E": 1780000000000, "r": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "s": "GEMI-CMB-260306-A1B2C3D4E5F6G7H8-CMB", "l": [ { "c": "123456", "o": "YES", "s": "GEMI-XRPUSD-260828" }, { "c": "123789", "o": "NO" } ], "n": "50000.00", "S": "OPEN", "w": 1780000001000, "x": 1780000120000, "c": 1780000000000 } ``` | Field | Type | Description | |---|---|---| | `e` | string | Event type — always `requestForQuote`. | | `E` | number | Event timestamp (milliseconds). | | `r` | string | `rfqId` — unique ULID for this request-for-quote. Pass it to `rfq.submit_quote`. | | `s` | string | The combo's deterministic instrument symbol, when available. Use it to place a CLOB order on the combo; this activates its book on demand. | | `l` | array | The contracts that make up the combo, in a fixed order. Each: `c` = CMS contract id, `o` = the outcome the combo takes on that contract (`YES` / `NO`), `s` = the leg's own instrument symbol, when available. | | `n` | string | Requested size as a USD notional, if sized that way. Omitted otherwise; mutually exclusive with `q`. | | `q` | string | Requested size in contracts, if sized that way. Omitted otherwise; mutually exclusive with `n`. The RFQ quantity grid can be whole contracts or `0.01` fractional contracts. | | `f` | string | Execution quantity calculated when the requester accepts. It is present on later lifecycle events and can appear on `FAILED`; only `S: "FINALIZED"` confirms successful execution. | | `S` | string | Lifecycle state (`OPEN` / `PENDING_ACCEPTANCE` / `CONFIRMING` / `FINALIZING` / `FINALIZED` / `CANCELLED` / `EXPIRED` / `FAILED`). `OPEN` is live and quotable. | | `w` | number | When the 1-second quoting window closes (milliseconds), 1 second after RFQ creation. Omitted when unset. | | `x` | number | Hard expiry by which the auction must finalize (milliseconds). Omitted when unset. | | `c` | number | When the request-for-quote was created (milliseconds). Omitted when unset. | :::note Exactly one of `n` (notional) and `q` (requested quantity) is present per event. The eventual fill is floored to the RFQ's quantity grid: whole contracts by default, or `0.01` fractional contracts when configured. An explicitly quantity-sized RFQ is already on that grid. ::: The public stream emits RFQ-existence transitions: creation, cancellation, close, expiry, finalization, and failure. It does not emit the requester acceptance or maker confirmation transitions, and it never carries quote contents. ### Replay on reconnect `requestForQuote` is an event stream, not a state snapshot. Auctions broadcast while you were disconnected are **not** replayed. Auctions are short-lived (the quoting window is carried in `w`), so treat a reconnect as starting fresh. ## `requestForQuote@account` Private combo-RFQ deliveries for the authenticated account. - **As a requester**: receive only the selected quote after your RFQ closes, followed by the winning maker's confirm/decline and the terminal outcome. - **As a maker**: receive the requester's acceptance of your quote and the terminal outcome. Respond to `ACCEPTED` with [`rfq.confirm_quote`](/prediction-markets/combos-rfq/quote-methods#rfqconfirm_quote) immediately; the 1-second deadline starts with the `CONFIRMING` transition, not delivery processing. The quoting window is sealed: quote submissions and withdrawals produce no customer delivery while the RFQ is `OPEN`. At close, the requester receives one `CLOSED` delivery containing only the selected quote. Losing quotes remain undisclosed, and the requester is not told which maker submitted the selected quote (it is keyed by `quoteId` only). The service does not assign session-level or RFQ-level participant pseudonyms. `r` identifies the RFQ and `q` identifies either the requested quantity or a quote, depending on the stream; neither identifies a participant. Counterparty account identities are not disclosed through these streams, including after execution. **Auth:** required, with the `view_orders` capability. See [Authentication](/prediction-markets/websocket/authentication). Mutually exclusive with `requestForQuote@session`. ### Message shape ```json { "e": "requestForQuote", "i": "b16097e9-3d7f-5910-9820-eeedf51a8be3", "E": 1780000001000, "r": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "x": "CLOSED", "S": "PENDING_ACCEPTANCE", "q": "01J9Z2K7Q2N8M4P6R8T0V2W4X6", "p": "0.55", "sz": "100", "qs": "ACTIVE", "vu": 1780000060000 } ``` | Field | Type | Description | |---|---|---| | `e` | string | Event type — always `requestForQuote`. | | `i` | string | Durable lifecycle-event UUID. Use it as an idempotency key when a transition is redelivered. | | `E` | number | Event timestamp (milliseconds). | | `r` | string | `rfqId` this delivery concerns. | | `x` | string | Which customer-visible lifecycle transition this delivery reports (`CLOSED` / `ACCEPTED` / `CONFIRMED` / `DECLINED` / `FINALIZED` / `FAILED`). `CLOSED` privately discloses the selected quote to the requester. | | `S` | string | Lifecycle state of the RFQ after this transition (same vocabulary as the public feed). | | `q` | string | `quoteId` this delivery concerns; the requester accepts by this id. Absent on RFQ-level deliveries. | | `p` | string | The quote's single-sided bid per contract. Absent on RFQ-level deliveries. | | `sz` | string | Quote quantity in contracts. Absent on RFQ-level deliveries. | | `qs` | string | Quote status (`ACTIVE` / `WITHDRAWN` / `EXPIRED` / `WON` / `LOST`). Absent on RFQ-level deliveries. | | `vu` | number | Quote close-time eligibility deadline (milliseconds). A selected quote had `vu >= w`; when the maker omitted `validUntil`, `vu` equals `w`. It is not checked again after selection. Absent on RFQ-level deliveries. | :::note Field keys `x` and `q` differ between the two streams On the public `requestForQuote` feed, `x` is the numeric hard-expiry timestamp and `q` is the requested contract quantity. On the authenticated deliveries, `x` is the string transition name and `q` is the `quoteId`. Parse per stream. ::: ### Redelivery and deduplication Authenticated lifecycle delivery is at least once. A transition can therefore arrive more than once, with the same `i`. Persist or otherwise deduplicate on `i`; do not use `E`, `r`, or `x` alone as the idempotency key. ## `requestForQuote@session` Identical account-scoped deliveries to `requestForQuote@account`, provided for subscription symmetry with the order streams. It does not filter RFQ events to the session that opened or quoted the RFQ. Subscribe to one or the other, not both — they are mutually exclusive on a connection. ## Connection notes - Same endpoint as all other Predictions streams: `wss://ws.gemini.com`. - Same subscribe message shape and connection parameters as the [Introduction](/prediction-markets/websocket/introduction). - The maker quote actions ride the same connection as request/response methods — see [Quote Methods](/prediction-markets/combos-rfq/quote-methods) and the general [Message Format](/prediction-markets/websocket/message-format). ## See also - [Quote Methods](/prediction-markets/combos-rfq/quote-methods) — submit, withdraw, confirm. - [Maker Integration](/prediction-markets/combos-rfq/maker-integration) — the full workflow tying streams and methods together. - [Standard Stream Reference](/prediction-markets/websocket/streams) — order, position, and balance streams for the resulting trades. --- URL: https://developer.gemini.com/prediction-markets/combos-rfq/quote-methods.md # Quote Methods :::caution The combo RFQ WebSocket methods documented on this page are available in production for beta testing. Quoting requires an eligible account with the required capabilities described below. ::: The maker quote actions are WebSocket **request/response methods** on the same authenticated connection that carries your streams. Requests and responses use the standard envelope documented in [Message Format](/prediction-markets/websocket/message-format): ```json { "id": 1, "method": "rfq.submit_quote", "params": { ... } } ``` All three methods require an [authenticated connection](/prediction-markets/websocket/authentication). The account identity on every call is the authenticated account — there is no account parameter. Each method also requires a specific trading capability on your session: `rfq.submit_quote` and `rfq.confirm_quote` require `place_orders`, while `rfq.withdraw_quote` requires `cancel_orders`. A call from a session missing the required capability is rejected with `-1004` (403). A key implication: a session scoped to `place_orders` but not `cancel_orders` can submit and confirm, but its `rfq.withdraw_quote` calls will fail with `-1004`. ## `rfq.submit_quote` Submit a quote on an open combo request-for-quote auction, discovered on the [`requestForQuote` stream](/prediction-markets/combos-rfq/websocket-streams#requestforquote). Quotes are **immutable and limited to one per account per RFQ** — they cannot be revised, and withdrawing does not free the slot. The quoting window is sealed: submission and withdrawal are not delivered to the requester or other makers while the auction is open. After close, the requester receives only the selected quote, keyed by `quoteId` and never attributed to your account; losing quotes remain undisclosed. Use the optional `clientId` to correlate the maker order with your own records. Gemini emits this value as `c` in the maker's authenticated `orderUpdate` fill event. If you omit it, Gemini uses a deterministic RFQ client order ID. The value must contain only printable ASCII characters and be at most 36 characters. Keep it unique for the maker account because Gemini does not deduplicate it. ### Parameters | Name | Type | Required | Description | |---|---|---|---| | `rfqId` | string | Yes | The request-for-quote to quote on, as broadcast on the `requestForQuote` stream. | | `price` | string | Yes | Single-sided bid per contract. Must be strictly between 0 and 1 and lie on the venue price tick; the default tick is `0.0001`. | | `quantity` | string | Yes | Maximum quantity this quote covers, in contracts. It must cover the complete request for the quote to be eligible at close; the RFQ quantity grid can be whole contracts or `0.01` fractional contracts. | | `validUntil` | integer | No | Close-time eligibility deadline as a Unix timestamp (ms). If supplied, it must be in the future. The quote remains eligible when `validUntil >= w`; a value before `w` expires it at close. If omitted, the service sets it to `w`. It is not checked after winner selection. | | `clientId` | string | No | Optional client order ID for tracking the maker fill. Gemini emits it as `c` in the fill's `orderUpdate` event. It must contain only printable ASCII characters and be at most 36 characters. Keep it unique for the maker account because Gemini does not deduplicate it. | ### Example ```json { "id": 1, "method": "rfq.submit_quote", "params": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "price": "0.55", "quantity": "1000", "validUntil": 1780000060000, "clientId": "maker-fill-123" } } ``` Response: ```json { "id": 1, "status": 200, "result": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "quoteId": "01J9Z2K7Q2N8M4P6R8T0V2W4X6" } } ``` Keep the returned `quoteId`: acceptances on the [`requestForQuote@account` stream](/prediction-markets/combos-rfq/websocket-streams#requestforquoteaccount) and the withdraw/confirm methods are keyed by it. At close, quote validity is evaluated against `w`, not the later time at which close processing happens. Eligible quotes are ranked by lowest price and then earliest submission. The venue checks maker collateral freshly in that order and falls through to the next candidate when necessary. A requester limit price, when present, is enforced during selection but is not exposed on the maker discovery stream. ### Errors | Code | Status | Meaning | |---|---|---| | `-1002` | 401 | Unauthorized — authentication required. | | `-1013` | 400 | Invalid parameters — check `rfqId`, `price`, `quantity`, `validUntil`, or `clientId`. | | `-2010` | 400 | Quote rejected — RFQ not found or no longer open, price off the tick grid, or a quote from this account already exists. | ## `rfq.withdraw_quote` Withdraw your own quote while the auction is still open and strictly before `w`. Withdrawal is **final for that RFQ**: a withdrawn quote is excluded from winner selection, cannot be reinstated, and does not free the one-quote-per-account slot. The withdrawal is not delivered to the requester or other makers. ### Parameters | Name | Type | Required | Description | |---|---|---|---| | `rfqId` | string | Yes | The request-for-quote the quote was submitted on. | | `quoteId` | string | Yes | The quote to withdraw, as returned by `rfq.submit_quote`. | ### Example ```json { "id": 2, "method": "rfq.withdraw_quote", "params": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "quoteId": "01J9Z2K7Q2N8M4P6R8T0V2W4X6" } } ``` Response: ```json { "id": 2, "status": 200, "result": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "quoteId": "01J9Z2K7Q2N8M4P6R8T0V2W4X6" } } ``` ### Errors | Code | Status | Meaning | |---|---|---| | `-1002` | 401 | Unauthorized — authentication required. | | `-1013` | 400 | Invalid parameters — check `rfqId` and `quoteId`. | | `-2010` | 400 | Withdraw rejected — RFQ or quote not found, not owned by this account, or the auction already closed. | ## `rfq.confirm_quote` The winning maker's **last-look**. When the requester accepts your quote, an `ACCEPTED` delivery arrives on the `requestForQuote@account` stream. The 1-second deadline begins when the RFQ enters `CONFIRMING`, so respond immediately: - `confirm: true` — authorize execution and proceed to settlement. - `confirm: false` — decline and fail the RFQ. An omitted `confirm` is rejected; it is never treated as an implicit decline. Both confirmation and explicit decline must complete strictly before the deadline. If neither does, the RFQ fails automatically. ### Parameters | Name | Type | Required | Description | |---|---|---|---| | `rfqId` | string | Yes | The request-for-quote awaiting your confirmation. | | `quoteId` | string | Yes | Your winning quote, as delivered on the `requestForQuote@account` stream. | | `confirm` | boolean | Yes | `true` confirms the trade and proceeds to settlement; `false` declines and fails the request-for-quote. | ### Example ```json { "id": 3, "method": "rfq.confirm_quote", "params": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "quoteId": "01J9Z2K7Q2N8M4P6R8T0V2W4X6", "confirm": true } } ``` Response: ```json { "id": 3, "status": 200, "result": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "quoteId": "01J9Z2K7Q2N8M4P6R8T0V2W4X6", "confirmed": true } } ``` ### Errors | Code | Status | Meaning | |---|---|---| | `-1002` | 401 | Unauthorized — authentication required. | | `-1013` | 400 | Invalid parameters — check `rfqId`, `quoteId`, and `confirm`. | | `-2010` | 400 | Confirm rejected — RFQ not awaiting this account's confirmation, or the quote is not the recorded winner. | ## Shared error behavior - `-1004` (403) — missing required capability (`place_orders` for submit/confirm, `cancel_orders` for withdraw). - `-1003` (429) — rate limited. Standard request-weight limits apply; see the [Introduction](/prediction-markets/websocket/introduction). - `-1101` (503) — the combo RFQ service is temporarily unavailable. Safe to retry with backoff. - `-1000` (500) — internal error. ## See also - [WebSocket Streams](/prediction-markets/combos-rfq/websocket-streams) — the discovery and delivery streams these methods pair with. - [Maker Integration](/prediction-markets/combos-rfq/maker-integration) — the full workflow. - [Message Format](/prediction-markets/websocket/message-format) — request/response envelope and general error codes. --- URL: https://developer.gemini.com/prediction-markets/combos-rfq/overview.md # Combos Request-for-Quote (RFQ) :::caution **Production availability:** The combo RFQ discovery and private-delivery streams and maker quote methods are available in production for beta testing. Quoting requires an eligible account with the capabilities described in [Quote Methods](/prediction-markets/combos-rfq/quote-methods). ::: ## What combo RFQ is Combo RFQ is a **private, sealed-bid auction** for multi-leg combo prediction contracts. A requester (taker) requests quotes for a bundle of two or more contracts. Market makers submit private quotes during an auction window. When the requester accepts the winning quote and the maker confirms, the trade settles. Auctions are sealed. Quote submissions and withdrawals remain hidden from all participants while the auction is open. At close, Gemini discloses only the winning quote to the requester. Losing quotes remain private. The maker interface runs entirely over WebSocket. Event streams broadcast RFQs and state updates, while request-response methods submit quotes over the same connection. ## End-to-end flow ``` Requester Venue Makers ───────── ───── ────── opens RFQ ───────────────► broadcast on requestForQuote ───► all subscribers (anonymous) ◄─── rfq.submit_quote ─────────── each interested maker (sealed while open; no quote deliveries) selected quote ◄────────── auction window closes; best eligible quote selected accepts winning quote ────► ACCEPTED delivered ────────────► winning maker only ◄─── rfq.confirm_quote ────────── winning maker (last-look) CONFIRMED delivered ◄───── trade proceeds to settlement ``` ## Lifecycle timing The auction operates across three decision windows. All actions must complete strictly before their respective deadlines; actions received at or after the deadline fail. | Phase | Window | |---|---| | Maker quoting | 1 second from RFQ creation. The public `w` timestamp is the quote-submission deadline. | | Requester decision | 5 seconds from the transition to `PENDING_ACCEPTANCE` to accept the selected quote. | | Maker confirmation | 1 second from the transition to `CONFIRMING` for the winning maker to confirm or decline. | The hard-expiry timestamp `x` is an absolute upper bound. If reached first, it shortens later windows. ## Winner selection When the quoting window ends, the venue considers only active quotes that: - were submitted before `w` and have `validUntil >= w`; - cover the complete requested size; - are at or below the requester's private limit price, when one is set; and - pass a fresh available-collateral check at close. Eligible quotes are ranked by lowest price, with an equal-price tie going to the earliest submission. The venue checks candidates in that order and selects the first maker with sufficient available collateral. If a candidate fails that fresh check, the venue continues to the next ranked quote. If none qualifies, the RFQ expires without disclosing a quote. Quote validity is evaluated against the logical close `w`, even if close processing runs later. Once a quote is selected, `validUntil` is not checked again during requester acceptance, maker confirmation, or finalization; those stages use their own lifecycle deadlines. ## Request lifecycle An RFQ moves through these states (`S` on every stream event): ``` OPEN ──► PENDING_ACCEPTANCE ──► CONFIRMING ──► FINALIZING ──► FINALIZED │ │ │ │ ├── CANCELLED ├── CANCELLED └── FAILED └── FAILED └── EXPIRED └── EXPIRED (declined or timed out) ``` | State | Meaning | |---|---| | `OPEN` | Live and quotable. Quotes are accepted during the 1-second quoting window, until `w` on the broadcast. | | `PENDING_ACCEPTANCE` | The window closed and a winning quote was selected; the requester has up to 5 seconds from this transition to accept. | | `CONFIRMING` | The requester accepted; the winning maker has up to 1 second from this transition to confirm or decline. | | `FINALIZING` | The maker confirmed; the venue is executing and settling the trade. | | `FINALIZED` | Execution completed successfully. `f` is present on the terminal broadcast. | | `CANCELLED` | The requester cancelled before accepting the winning quote. | | `EXPIRED` | No feasible quote existed at the window close, or the requester did not accept the winner in time. | | `FAILED` | The maker declined or timed out, or execution could not be completed. | :::note `f` can also be present on a `FAILED` event because the execution quantity is calculated when the requester accepts. Only `S: "FINALIZED"` confirms successful execution; do not infer a fill from `f` alone. ::: :::caution Requester full-size does not guarantee maker full-size The requester's execution order is fill-or-kill and must fill in full for the RFQ to finalize. The selected maker's post-only order executes independently on the normal order book and may fill partially or not at all. A maker partial fill can remain even if the requester order fails and the RFQ ends in `FAILED`; only the maker order's unfilled remainder is cancelled. See [Execution and partial-fill risk](/prediction-markets/combos-rfq/maker-integration#execution-and-partial-fill-risk). ::: ## Anonymity and privacy guarantees - The public `requestForQuote` feed carries only the auction's existence and parameters — **no requester identity, no maker identities, no quote contents**. - While the auction is open, a quote is visible only to the maker who submitted it; the requester receives no submission or withdrawal events. - After close, only the selected quote is disclosed privately to the requester. Losing quotes remain undisclosed. - The requester is never told which maker quoted: quotes are keyed by `quoteId` only. - The service does not assign participant pseudonyms. `rfqId` identifies an auction and `quoteId` identifies a quote; neither identifies a participant or account. - RFQ streams do not disclose counterparty account identity, including after execution. ## Surface map | You want to... | Use | |---|---| | Discover open RFQ auctions | [`requestForQuote` stream](/prediction-markets/combos-rfq/websocket-streams#requestforquote) (public) | | Receive the selected quote on your RFQ or acceptance of your quote | [`requestForQuote@account` stream](/prediction-markets/combos-rfq/websocket-streams#requestforquoteaccount) (authenticated) | | Submit a quote | [`rfq.submit_quote`](/prediction-markets/combos-rfq/quote-methods#rfqsubmit_quote) | | Withdraw your quote | [`rfq.withdraw_quote`](/prediction-markets/combos-rfq/quote-methods#rfqwithdraw_quote) | | Confirm or decline after winning | [`rfq.confirm_quote`](/prediction-markets/combos-rfq/quote-methods#rfqconfirm_quote) | ## Glossary | Term | Meaning | |---|---| | RFQ | One request-for-quote auction, identified by `rfqId` (a ULID). | | Leg | One prediction-market contract inside the combo, with the outcome (`YES`/`NO`) the combo takes on it. | | Notional | RFQ sized as a USD amount (`n`). Mutually exclusive with requested quantity. | | Requested quantity | RFQ sized as a contract quantity (`q`). The quantity grid can be whole contracts or `0.01` fractional contracts; mutually exclusive with notional. | | Quote | A maker's private, immutable price + maximum quantity on an RFQ. One per account per RFQ. | | Last-look | The winning maker's confirm/decline step after the requester accepts. | ## Read next - [WebSocket Streams](/prediction-markets/combos-rfq/websocket-streams) — message shapes for the discovery and delivery streams. - [Quote Methods](/prediction-markets/combos-rfq/quote-methods) — submit, withdraw, and confirm. - [Maker Integration](/prediction-markets/combos-rfq/maker-integration) — the end-to-end maker workflow. - [Examples](/prediction-markets/combos-rfq/examples) — a worked auction from broadcast to settlement. - [Combo Contracts](/prediction-markets/combo-contracts/overview) — what a combo is and how it settles. --- URL: https://developer.gemini.com/prediction-markets/combos-rfq/maker-integration.md # Maker Integration :::caution The combo RFQ WebSocket discovery stream, private-delivery streams, and quote methods described here are available in production for beta testing. Quoting requires an eligible account with the capabilities described in [Quote Methods](/prediction-markets/combos-rfq/quote-methods). ::: ## Who can quote The discovery feed is public — no authentication is required to subscribe. Private deliveries require `view_orders`; submitting and confirming quotes require `place_orders`; withdrawing a quote requires `cancel_orders`. Quoting is **all-WebSocket**: one authenticated connection can carry discovery, private deliveries, and the quote actions. ## The complete maker workflow ``` 1. Connect to wss://ws.gemini.com (authenticated) 2. Subscribe to requestForQuote (public discovery feed) 3. Subscribe to requestForQuote@account (your private deliveries) ── for each requestForQuote event with S == "OPEN" that matches your interest ── 4. Price the combo from its legs (l: contract id + YES/NO outcome per leg) 5. rfq.submit_quote { rfqId, price, quantity, clientId? } within the 1-second quoting window → keep the returned quoteId 6. (optional) rfq.withdraw_quote if you need to pull the quote before the window closes ── if your quote wins and the requester accepts ── 7. An ACCEPTED delivery arrives on requestForQuote@account carrying your quoteId 8. rfq.confirm_quote { rfqId, quoteId, confirm: true } within 1 second 9. Watch the same stream for FINALIZED (fill quantity arrives on the public feed's f) ``` Pseudocode: ```js ws.subscribe(["requestForQuote", "requestForQuote@account"]) const myQuotes = new Map() // rfqId -> quoteId ws.onMessage(async (msg) => { if (msg.e !== "requestForQuote") return // Public discovery: a new auction to consider. if (msg.S === "OPEN" && msg.l && !myQuotes.has(msg.r)) { if (!interestedIn(msg.l)) return const price = priceCombo(msg.l) // your pricing model const quantity = sizeFor(msg.n ?? msg.q, price) // notional XOR quantity sizing const quote = { rfqId: msg.r, price: price.toFixed(4), quantity: String(quantity), clientId: `rfq-${msg.r}`, // optional; printable ASCII, max 36 chars } // Omit validUntil to use w, the service-assigned default. const res = await ws.call("rfq.submit_quote", quote) myQuotes.set(msg.r, res.quoteId) return } // Private delivery: the requester accepted this account's quote. if (msg.x === "ACCEPTED" && msg.q === myQuotes.get(msg.r)) { const ok = await lastLook(msg) // re-check your price/risk await ws.call("rfq.confirm_quote", { rfqId: msg.r, quoteId: msg.q, confirm: ok, }) } }) ``` For a complete typed server implementation with exact decimal arithmetic, RFQ/delivery deduplication, a pluggable pricing policy, and an explicit last-look hook, see the [TypeScript SDK RFQ example](/tools/typescript-sdk/deep-dives/rfq#complete-maker-example-with-a-pricing-hook). ## Quoting rules - **One immutable quote per account per RFQ.** No revisions; withdrawing does not free the slot. Price it right the first time. - **Price is per contract, strictly between 0 and 1**, on the venue price tick. The default tick is `0.0001`. - **Quantity is your maximum and must cover the complete request to be eligible.** A notional-sized RFQ requires `quantity × price >= notional`; a quantity-sized RFQ requires `quantity >= requestedQuantity`. The execution size is floored to the RFQ's quantity grid: whole contracts by default, or `0.01` fractional contracts when configured. - **`validUntil` is a close-time eligibility deadline.** If supplied, it must be in the future. A quote remains eligible when `validUntil >= w`; a value before `w` expires the quote at close. If omitted, the service sets it to `w`. It is not checked again after winner selection. - **The auction is side-effect-free while quoting.** Submission performs a collateral preflight but does not reserve funds. At close, the venue freshly checks candidates in price-time order and falls through to the next quote when a maker lacks available collateral. The requester and maker collateral holds are placed later, at requester acceptance and maker confirmation respectively. ## Execution and partial-fill risk :::caution The RFQ's full-size guarantee applies to the requester's fill-or-kill order, **not** to the selected maker's post-only order. After the maker confirms, the venue places the maker's order first. Other market participants can trade against it before the requester's order arrives, so the maker can receive a partial fill. If the requester order then fails, the venue cancels only the maker order's unfilled remainder; completed maker fills remain and may create or change a position even though the RFQ ends in `FAILED`. The reverse is also possible: the requester can fill in full against other resting liquidity, allowing the RFQ to reach `FINALIZED` while the selected maker fills only part, or none, of the quoted quantity. Monitor the standard authenticated order, position, and balance streams for actual maker fills rather than inferring them from the RFQ state or `f`. ::: ## Timing The lifecycle uses a 1-second quoting window, a 5-second requester decision window, and a 1-second maker confirmation window. The authenticated acceptance does not carry a separate confirmation-deadline field. The confirmation window starts when the RFQ enters `CONFIRMING`, so respond immediately when the `ACCEPTED` delivery arrives. | Phase | Timing | |---|---| | Maker quoting | Submit or withdraw strictly before `w` on the public `requestForQuote` broadcast. `w` is 1 second after RFQ creation. | | Your quote's validity | Quote-scoped private deliveries mirror `validUntil` in `vu`. The quote is eligible when `validUntil >= w`; omitting it assigns `w`. The field has no effect after selection. | | Requester decision | The requester has 5 seconds from the `PENDING_ACCEPTANCE` transition to accept the selected quote. | | Maker confirmation | Confirm or decline strictly before the 1-second deadline measured from the `CONFIRMING` transition; otherwise the RFQ fails. | | Hard auction expiry | `x` (number) on the public broadcast is the absolute upper bound and can shorten either post-close window. | Authenticated lifecycle deliveries include a durable event ID in `i`. Treat `i` as an idempotency key because an at-least-once delivery can repeat the same transition. ## Anonymity - The requester receives no quote submission or withdrawal events while the auction is open. After close, only the selected quote is disclosed; losing quotes remain undisclosed. - Your identity is never revealed to the requester — quotes are keyed by `quoteId` only. - The public feed never carries quote contents; losing quotes are never disclosed. ## What's not covered here - **Opening RFQs** (the requester side) is a retail-facing flow in the Gemini app and website; there is no public API for creating RFQs. - **Settlement mechanics** of the resulting combo position follow the standard combo contract lifecycle — see [Combo Contracts](/prediction-markets/combo-contracts/overview). ## See also - [Quote Methods](/prediction-markets/combos-rfq/quote-methods) — full parameter and error tables. - [WebSocket Streams](/prediction-markets/combos-rfq/websocket-streams) — message shapes. - [Examples](/prediction-markets/combos-rfq/examples) — a worked auction end to end. --- URL: https://developer.gemini.com/prediction-markets/combos-rfq/examples.md # Examples :::caution The combo RFQ WebSocket streams and quote methods used in these examples are available in production for beta testing. Quoting requires an eligible account with the capabilities described in [Quote Methods](/prediction-markets/combos-rfq/quote-methods). ::: A complete two-leg auction from the winning maker's perspective. All timestamps are milliseconds; all prices and quantities are string decimals. ## 1. Discovery A taker opens a $50,000 notional RFQ on a two-leg combo. The anonymous broadcast arrives on `requestForQuote`: ```json { "e": "requestForQuote", "E": 1780000000000, "r": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "l": [ { "c": "123456", "o": "YES", "s": "GEMI-XRPUSD-260828" }, { "c": "123789", "o": "NO" } ], "n": "50000.00", "S": "OPEN", "w": 1780000001000, "x": 1780000120000, "c": 1780000000000 } ``` The quoting window closes at `w` — 1 second after creation. ## 2. Submit a quote The maker prices the combo at 0.55 per contract and will cover up to 100,000 contracts: ```json { "id": 1, "method": "rfq.submit_quote", "params": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "price": "0.55", "quantity": "100000", "clientId": "maker-fill-123" } } ``` ```json { "id": 1, "status": 200, "result": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "quoteId": "01J9Z2K7Q2N8M4P6R8T0V2W4X6" } } ``` The maker supplied `clientId` to correlate the eventual maker order with its own records. Gemini returns the same value as `c` on the maker's authenticated `orderUpdate` fill event. :::note The quote is immutable — there is no revise. If the maker needs out before the window closes, `rfq.withdraw_quote` pulls it — permanently, for this RFQ (the one-quote-per-account slot is not freed). ::: The submission response is private to the maker. Neither submission nor withdrawal produces a customer stream delivery while the RFQ is open. Because the maker omitted `validUntil`, the service assigns `w` (`1780000001000`). That makes the quote eligible at the logical close; `validUntil` is not checked again in later lifecycle stages. ## 3. Winner disclosed to the requester At or after `w`, the venue selects the best eligible quote. This example closes 100 milliseconds after `w`. The requester receives one private `CLOSED` delivery containing only that quote; losing quotes remain undisclosed: ```json { "e": "requestForQuote", "i": "b16097e9-3d7f-5910-9820-eeedf51a8be3", "E": 1780000001100, "r": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "x": "CLOSED", "S": "PENDING_ACCEPTANCE", "q": "01J9Z2K7Q2N8M4P6R8T0V2W4X6", "p": "0.55", "sz": "100000", "qs": "ACTIVE", "vu": 1780000001000 } ``` The transition to `PENDING_ACCEPTANCE` starts the requester's 5-second decision window. Notice that `vu` is already at the logical auction close; that does not prevent post-close acceptance. ## 4. Acceptance arrives In this example, the requester accepts 3 seconds after the `CLOSED` transition. The maker's `requestForQuote@account` stream delivers: ```json { "e": "requestForQuote", "i": "88bfe6b3-be05-56c6-bd1b-51b25fafb27a", "E": 1780000004100, "r": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "x": "ACCEPTED", "S": "CONFIRMING", "q": "01J9Z2K7Q2N8M4P6R8T0V2W4X6", "p": "0.55", "sz": "100000", "qs": "ACTIVE", "vu": 1780000001000 } ``` On the whole-contract quantity grid used in this example, at $50,000 notional and a price of 0.55, the fill is `floor(50000 / 0.55)` = **90,909 contracts**. The `ACCEPTED` transition and matching `quoteId` identify the winner. The quote remains `ACTIVE` until execution finalizes, when its status becomes `WON`. ## 5. Last-look confirm The transition to `CONFIRMING` starts the winning maker's 1-second deadline to confirm or decline. The maker should act immediately on the `ACCEPTED` delivery. ```json { "id": 2, "method": "rfq.confirm_quote", "params": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "quoteId": "01J9Z2K7Q2N8M4P6R8T0V2W4X6", "confirm": true } } ``` ```json { "id": 2, "status": 200, "result": { "rfqId": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "quoteId": "01J9Z2K7Q2N8M4P6R8T0V2W4X6", "confirmed": true } } ``` Confirming places the maker's collateral hold, authorizes execution, and hands the trade to settlement (`S` moves to `FINALIZING`). Declining instead (`"confirm": false`) fails the RFQ: the requester is notified, and no trade occurs. ## 6. Finalization The public feed (and the maker's private stream) reports the terminal state, now carrying the filled quantity: ```json { "e": "requestForQuote", "E": 1780000004500, "r": "01J9Z3K7Q2N8M4P6R8T0V2W4X6", "l": [ { "c": "123456", "o": "YES", "s": "GEMI-XRPUSD-260828" }, { "c": "123789", "o": "NO" } ], "n": "50000.00", "f": "90909", "S": "FINALIZED", "c": 1780000000000 } ``` The resulting combo position and balance changes flow through the standard authenticated streams — see the [Stream Reference](/prediction-markets/websocket/streams). :::caution Maker fill can differ from the requester fill `f: "90909"` is the requester's full-size execution quantity; it does not guarantee that the selected maker filled 90,909 contracts. The maker's post-only order executes first on the normal order book and can fill partially against other participants, while the requester can obtain some or all of its fill from other resting liquidity. If requester execution fails after a maker partial fill, the completed maker fill remains even though the RFQ ends in `FAILED`; only the unfilled maker-order remainder is cancelled. ::: ## Error example: quoting a closed auction Submitting after the window closes: ```json { "id": 3, "status": 400, "error": { "code": -2010, "msg": "RFQ is not in a valid state for this operation" } } ``` ## See also - [Quote Methods](/prediction-markets/combos-rfq/quote-methods) — full parameter and error tables. - [Maker Integration](/prediction-markets/combos-rfq/maker-integration) — the workflow these messages belong to. --- URL: https://developer.gemini.com/prediction-markets/combo-contracts/overview.md # Combo Contracts Combo contracts let you take a single position on the joint outcome of multiple existing contracts. A combo settles YES only if every leg settles YES — otherwise it settles NO. Each combo trades on its own continuous limit orderbook with its own deterministic ticker. **Example:** A trader who believes BTC will be above $120,000 at year-end and ETH will be above $5,000 at year-end can combine: - **Leg 1:** `GEMI-BTC-EOY26-HI120000` — YES - **Leg 2:** `GEMI-ETH-EOY26-HI5000` — YES Both are existing single-contract markets with their own independent orderbooks. The combo is a separate, third instrument that settles based on their joint outcome. ## Contract Specification | Property | Value | |----------|-------| | Leg source | Existing contracts already listed on Gemini Predictions | | Logic | AND only (at launch) | | Leg direction | `Yes` or `No` outcome per underlying contract | | Leg count | 2–6 | | Settlement formula | Multiplicative — payoff is the product of leg outcomes | | Settlement values | Each leg contributes 1 when it settles to its required outcome, or 0 otherwise | | Tick size | $0.01, same as single contracts | | Price range | $0.01 – $0.99 | OR / XOR / conditional logic are not available at launch and will be evaluated after launch. ## Ticker Format Combo tickers are deterministic and content-addressable: ``` GEMI-CMB-{MMYY}-{HASH12} ``` | Component | Description | |-----------|-------------| | `GEMI` | Gemini namespace | | `CMB` | Combo identifier | | `{MMYY}` | Two-digit month and year of first creation | | `{HASH12}` | 12-character hex hash of the canonical leg set | **Example:** `GEMI-CMB-0526-A7F3B2C1D4E5` ## Pricing The fair value of a combo under independence is the product of its leg prices: ``` P(combo) = P(leg₁) × P(leg₂) × ... × P(legₙ) ``` For example, a two-leg combo with legs trading at $0.60 and $0.70 has a fair value of $0.42 under independence. Correlation between legs (positive or negative) will move the market price away from this anchor; the orderbook reflects participants' collective view on the joint probability. Because the same multiplicative formula governs settlement, there is no structural wedge between pricing math and settlement math. ## Settlement ### State machine A combo proceeds through the following states based on the resolution of its legs: - **Active** — At least one leg has not yet resolved, and no leg has resolved NO - **Settled: YES** — Every leg has resolved YES - **Settled: NO** — At least one leg has resolved NO ### Settlement value | Outcome | Settlement value | |---------|-----------------| | YES (all legs settle YES) | $1.00 | | NO (any leg settles NO) | $0.00 | ## Orderbook Behavior Each combo is a distinct market with its own orderbook. Combos and their underlying legs are separate instruments and do not interact at the matching layer. - **No cross-market matching.** Orders on A ∩ B ∩ C do not match against orders on A ∩ B or on the individual legs A, B, or C. Each combo is its own instrument with its own resting depth. - **No duplicate markets.** If the same combination of legs already exists, requests route to the existing market rather than creating a new one. - **Independent liquidity.** Each combo's depth and spread is independent of its underlying legs. Combos can be wider than their legs, especially for novel combinations. ## Discovery Gemini Predictions supports both discovery of existing combos and authenticated creation of a canonical custom combo. A request with the same complete set of legs resolves to the existing combo rather than creating a duplicate. Combos are available through the API: - Listing endpoints expose active combo tickers alongside their leg breakdowns - The authenticated create endpoint creates or retrieves a canonical two-to-six-leg combo - Market data feeds publish combo orderbook state on the same channels as single contracts - Order entry treats combo tickers as first-class instruments ## API Combo-specific endpoints: | Endpoint | Description | |----------|-------------| | [`GET /v1/prediction-markets/combos`](/rest-api/prediction-markets/combos/list-combos) | List active combos with leg breakdowns | | [`GET /v1/prediction-markets/combos/{instrumentSymbol}`](/rest-api/prediction-markets/combos/get-combo) | Resolve a combo ticker to its full leg specification and per-leg resolution status | | [`POST /v1/prediction-markets/combos`](/rest-api/prediction-markets/combos/create-combo) | Create a canonical combo or retrieve the existing combo for the same complete leg set | ## FAQ **What contracts can be used as combo legs?** Any contract currently listed and trading on Gemini Predictions can serve as a combo leg. Combos do not introduce new underlying instruments — they reference existing markets. **Can I trade in and out of a combo before all legs resolve?** Yes, as long as there is liquidity. Combos trade continuously on their orderbook until the combo settles, so you can close a position at any time before final settlement provided there are resting orders to trade against. **What happens to my position if one leg resolves early?** If the early-resolving leg settles NO, the combo settles NO immediately. If it settles YES, the combo continues trading; remaining uncertainty now lies entirely with the unresolved legs, and the market price will typically re-price to reflect this. **Can I create a combo with the same leg twice?** No. Each leg in a combo must reference a unique underlying contract. **Can combos include other combos as legs?** No. Legs must be single contracts. **Why does my order on combo A∩B not interact with my order on contract A?** Combos and their underlying singles are separate instruments with separate orderbooks. Matching happens within each instrument's book independently. This allows the combo's market price to reflect joint-probability views distinct from the leg prices. **Are combo fees different from single-contract fees?** Combo fees follow the same schedule as single contracts. See the [fee schedule](https://www.gemini.com/fees/predictions) for details. **If I hold a YES position on leg A individually and also hold a combo containing leg A, are those positions related?** No. A combo and its underlying single contracts are independent instruments with separate positions. Holding YES on `GEMI-BTC-EOY26-HI120000` directly and holding YES on a combo that includes that leg are two distinct position entries. Settlement of the single does not net against or affect your combo position. --- URL: https://developer.gemini.com/trading/rest-api/staking/unstake-crypto-funds.md # Unstake Crypto Funds " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "v1/staking/unstake", nonce: "", providerId: "62b21e17-2534-4b9f-afcf-b7edb609dd8d", currency: "MATIC", amount: "20", }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Trader or Fund Manager role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/staking/stake-crypto-funds.md # Stake Crypto Funds " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "v1/staking/stake", nonce: "", providerId: "62b21e17-2534-4b9f-afcf-b7edb609dd8d", currency: "MATIC", amount: "30", }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Trader or Fund Manager role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/staking/list-staking-rewards.md # List Staking Rewards " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/staking/rewards", nonce: "", since: "2022-08-20T00:00:00.000Z", until: "2022-11-05T00:00:00.000Z", providerId: "62b21e17-2534-4b9f-afcf-b7edb609dd8d", currency: "ETH", }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/staking/list-staking-rates.md # List Staking Rates , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/staking/list-staking-event-history.md # List Staking Event History " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/staking/history", nonce: "", account: "primary", since: "2022-11-01T00:00:00.000Z", until: "2022-11-03T00:00:00.000Z", limit: 50, }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned. See Roles for more information.

), }, { heading: "How to iterate through all transactions", children: (
  1. Initial request: POST to https://api.gemini.com/v1/staking/history with a JSON payload including sortAsc set to false and a limit of 500.
  2. The response is sorted by datetime descending — the last element has the lowest timestamp. Call it X.
  3. Send a second request with until set to X-1, sortAsc still false, and limit of 500.
  4. Repeat using the last element's timestamp minus one as the new until value.
  5. Continue until an empty list is returned.
), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/staking/list-staking-balances.md # List Staking Balances " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/balances/staking", nonce: "", account: "primary", }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/wrap-order.md # Wrap Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/wrap/GUSDUSD", nonce: "", amount: "1", side: "buy", client_order_id: "4ac6f45f-baf1-40f8-83c5-001e3ea73c7f", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/list-past-trades.md # List Past Trades

Gemini recommends using our WebSocket Order Events API to be notified when a trade executes on your account instead of polling this endpoint.

Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting.

Enabled for perpetuals accounts from July 10th, 0100hrs ET onwards. Trade info for all perpetuals orders submitted prior to this timing will not be available through this API.

) }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/mytrades", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/mytrades", nonce: "", symbol: "btcusd", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have history:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "How to retrieve your trade history", children: (

To retrieve your full trade history walking backwards:

  1. Initial request: POST to https://api.gemini.com/v1/mytrades with a JSON payload including a timestamp key with value 0 and a limit_trades key with value 500.
  2. The list is sorted by timestamp descending. Take the highest timestamp value X from the first element.
  3. Create a second request with timestamp set to X+1 and limit_trades set to 500.
  4. Repeat, using the highest timestamp from each response, until an empty list is returned.

), }, { heading: "Break Types", children: (

In the rare event that a trade has been reversed (broken), the trade that is broken will have this flag set. The field will contain one of these values:

  • manual — The trade was reversed manually. All fees, proceeds, and debits associated with the trade have been credited or debited to the account separately. This reported trade must be included for the account balance to be correct.
  • full — The trade was fully broken. The reported trade should not be accounted for. It will be as though the transfer of funds associated with the trade had simply not happened.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/list-past-orders.md # List Past Orders " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/orders/history", nonce: "", limit_orders: 50, }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have history:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "How to retrieve your order history", children: (

To retrieve your full order history walking backwards:

  1. Initial request: POST to https://api.gemini.com/v1/orders/history with a JSON payload including a timestamp key with value 0 and a limit_orders key with value 500.
  2. The list is sorted by timestamp descending. Take the highest timestamp value X from the first element.
  3. Create a second request with timestamp set to X+1 and limit_orders set to 500.
  4. Repeat, using the highest timestamp from each response, until an empty list is returned.

), }, { heading: "Break Types", children: (

In the rare event that a trade has been reversed (broken), the trade that is broken will have this flag set. The field will contain one of these values:

  • manual — The trade was reversed manually. All fees, proceeds, and debits associated with the trade have been credited or debited to the account separately. This reported trade must be included for the account balance to be correct.
  • full — The trade was fully broken. The reported trade should not be accounted for. It will be as though the transfer of funds associated with the trade had simply not happened.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/list-active-orders.md # List Active Orders

Gemini recommends using our WebSocket Order Events API to maintain a current view of your active orders. It's both faster and more efficient than polling this endpoint.

Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting.

Enabled for perpetuals accounts from July 10th, 0100hrs ET onwards.

) }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/orders", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/orders", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have orders:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/heartbeat.md # Heartbeat " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/heartbeat", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

Any authenticated API key may call this endpoint. See Require Heartbeat for background on the heartbeat flag and session behavior.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/get-trading-volume.md # Get Trading Volume " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/tradevolume", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have history:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/get-order-status.md # Get Order Status

Gemini recommends using our WebSocket Order Events API to receive order status changes. It's much better because you'll be notified of order status changes as they happen.

Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting.

Enabled for perpetuals accounts from July 10th, 0100hrs ET onwards. Trade info for all perpetuals orders submitted prior to this timing, will not be available through this API.

) }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/order/status", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/order/status", nonce: "", order_id: 123456789012345, include_trades: true, }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

The OAuth scope must have orders:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/get-notional-trading-volume.md # Get Notional Trading Volume " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/notionalvolume", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have history:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/create-new-order.md # Create New Order If you wish orders to be automatically cancelled when your session ends, see the require heartbeat section, or manually send the cancel all session orders message.} note={{ title: "Note", children: <>

Master API keys do not support cancelation on disconnect via heartbeat.

Enabled for perpetuals accounts from July 10th, 0100hrs ET onwards.

}} exampleNode={} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Margin Orders", children: ( <>

Set margin_order: true to place an order using borrowed funds on a margin-enabled account. This allows you to trade with leverage beyond your available balance.

Important

Margin trading amplifies both gains and losses. Monitor your account using the Margin Account Summary endpoint and preview order impacts with Order Preview before placing margin orders.

), }, { heading: "Stop-Limit Orders", children: (

A Stop-Limit order is an order type that allows for order placement when a price reaches a specified level. Stop-Limit orders take in both a price and a stop_price as parameters. The stop_price is the price that triggers the order to be placed on the continuous live order book at the price. For buy orders, the stop_price must be below the price while sell orders require the stop_price to be greater than the price.

), }, { heading: "What about market orders?", children: ( <>

The API doesn't directly support market orders because they provide you with no price protection.

Instead, use the "immediate-or-cancel" order execution option, coupled with an aggressive limit price (i.e. very high for a buy order or very low for a sell order), to achieve the same result.

), }, { heading: "Order execution options", children: ( <>

Note that options is an array. If you omit options or provide an empty array, your order will be a standard limit order - it will immediately fill against any open orders at an equal or better price, then the remainder of the order will be posted to the order book.

If you specify more than one option (or an unsupported option) in the options array, the exchange will reject your order.

No options can be applied to stop-limit orders at this time.

The available limit order options are:

Option
Description
"maker-or-cancel"

This order will only add liquidity to the order book.

If any part of the order could be filled immediately, the whole order will instead be canceled before any execution occurs.

If that happens, the response back from the API will indicate that the order has already been canceled ("is_cancelled": true in JSON).

Note: some other exchanges call this option "post-only".

"immediate-or-cancel"

This order will only remove liquidity from the order book.

It will fill whatever part of the order it can immediately, then cancel any remaining amount so that no part of the order is added to the order book.

If the order doesn't fully fill immediately, the response back from the API will indicate that the order has already been canceled ("is_cancelled": true in JSON).

"fill-or-kill"

This order will only remove liquidity from the order book.

It will fill the entire order immediately or cancel.

If the order doesn't fully fill immediately, the response back from the API will indicate that the order has already been canceled ("is_cancelled": true in JSON).

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/cancel-order.md # Cancel Order Enabled for perpetuals accounts from July 10th, 0100hrs ET onwards.

}} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/order/cancel", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/order/cancel", nonce: "", order_id: 106817811, }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "All Cancellation Reasons", children: ( <>

Under unique circumstances, orders may be automatically cancelled by the exchange. These scenarios are detailed in the table below:

Cancel Reason
Description
MakerOrCancelWouldTake
Occurs when the "maker-or-cancel" execution option is included in the order request and any part of the requested order could be filled immediately.
ExceedsPriceLimits
Occurs when there is not sufficient liquidity on the order book to support the entered trade. Orders will be automatically cancelled when liquidity conditions are such that the order would move price +/- 5%.
SelfCrossPrevented
Occurs when a user enters a bid that is higher than that user's lowest open ask or enters an ask that is lower than their highest open bid on the same pair.
ImmediateOrCancelWouldPost
Occurs when the "immediate-or-cancel" execution option is included in the order request and the requested order cannot be fully filled immediately. This type of cancellation will only cancel the unfulfilled part of any impacted order.
FillOrKillWouldNotFill
Occurs when the "fill-or-kill" execution option is included in the new order request and the entire order cannot be filled immediately. Unlike "immediate-or-cancel" orders, this execution option will result in the entire order being cancelled rather than just the unfulfilled portion.
Requested
Cancelled via user request to /v1/order/cancel endpoint.
MarketClosed
Occurs when an order is placed for a trading pair that is currently closed.
TradingClosed
Occurs when an order is placed while the exchange is closed for trading.
), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/cancel-all-session-orders.md # Cancel All Session Orders This will cancel all orders opened by this session. This will have the same effect as heartbeat expiration if "Require Heartbeat" is selected for the session.} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/order/cancel/session", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/order/cancel/session", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/orders/cancel-all-active-orders.md # Cancel All Active Orders This will cancel all outstanding orders created by all sessions owned by this account, including interactive orders placed through the UI.} note={{ title: "Note", children:

Note that this cancels orders that were not placed using this API key. Enabled for perpetuals accounts from July 10th, 0100hrs ET onwards. Typically Cancel All Session Orders is preferable, so that only orders related to the current connected session are cancelled.

}} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/order/cancel/all", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/order/cancel/all", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/margin/preview-margin-order.md # Preview Margin Order Impact This endpoint is only available for margin trading accounts.

}} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/margin/order/preview", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/margin/order/preview", nonce: "", symbol: "btcusd", side: "buy", type: "limit", amount: "0.5", price: "50000.00", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have orders:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/margin/get-margin-interest-rates.md # Get Margin Interest Rates This endpoint is only available for margin trading accounts.

}} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/margin/rates", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/margin/rates", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader, Fund Manager or Auditor role assigned. See Roles for more information.

The OAuth scope must have balances:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/margin/get-margin-account-summary.md # Get Margin Account Summary This endpoint is only available for margin trading accounts. Standard exchange accounts will receive an error.

}} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/margin/account", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/margin/account", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader, Fund Manager or Auditor role assigned. See Roles for more information.

The OAuth scope must have balances:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/list-trades.md # List Trades

This public API endpoint is limited to retrieving seven calendar days of data.

Please contact us for information about Gemini market data.

) }} example={{ request: { method: "GET", url: "https://api.gemini.com/v1/trades/BTCUSD", headers: [], }, }} sections={[ { heading: "Path Parameters", children: , }, { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/list-symbols.md # List Symbols , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/list-prices.md # List Prices , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/list-fee-promos.md # List Fee Promos , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/list-derivative-candles.md # List Derivative Candles , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/list-candles.md # List Candles , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/get-ticker.md # Get Ticker We recommend using Version 2 to retrieve recent ticker activity.

}} example={{ request: { method: "GET", url: "https://api.gemini.com/v1/pubticker/BTCUSD", headers: [], }, }} sections={[ { heading: "Path Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/get-symbol-details.md # Get Symbol Details , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/get-network.md # Get Network The v1 network endpoint is being retired. This v2 endpoint is the recommended replacement, offering account-level filtering for deposit and withdraw access. Please migrate to this endpoint at your earliest convenience.

}} example={{ request: { method: "GET", url: "https://api.gemini.com/v2/network/USDC", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Fund Manager or Auditor role assigned. See Roles for more information.

), }, { heading: "Path Parameters", children: , }, { heading: "Headers", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/get-current-order-book.md # Get Current Order Book The quantities and prices returned are returned as strings rather than numbers. The numbers returned are exact, not rounded, and it can be dangerous to treat them as floating point numbers.

}} example={{ request: { method: "GET", url: "https://api.gemini.com/v1/book/BTCUSD", headers: [], }, }} sections={[ { heading: "Path Parameters", children: , }, { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/get-assets-for-network.md # Get Assets for Network , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/market-data/fx-rate.md # FX Rate Gemini does not offer foreign exchange services. This endpoint is for historical reference only and does not provide any guarantee of future exchange rates.

}} example={{ request: { method: "GET", url: "https://api.gemini.com/v2/fxrate/AUDUSD/1594651859000", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Auditor role assigned. See Roles for more information.

), }, { heading: "Supported Pairs", children: (

AUDUSD{" "}CADUSD{" "}COPUSD{" "}EURUSD{" "}CHFUSD{" "}HKDUSD{" "}NZDUSD{" "}GBPUSD{" "}BRLUSD{" "}INRUSD{" "}SGDUSD{" "}KRWUSD{" "}JPYUSD{" "}CNYUSD

), }, { heading: "Path Parameters", children: , }, { heading: "Headers", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/instant-orders/get-instant-quote.md # Get Instant Quote " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/instant/quote", nonce: "", symbol: "btcusd", side: "buy", totalSpend: "100", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/instant-orders/execute-instant-order.md # Execute Instant Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/instant/execute", nonce: "", symbol: "BTCUSD", side: "buy", quantity: "0.01505181", price: "6445.07", fee: "2.9900309233", quoteId: 1328, }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/withdraw-crypto-funds.md # Withdraw Crypto Funds " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v2/withdraw/ethereum/eth", nonce: "", address: "0xA63123350Acc8F5ee1b1fBd1A6717135e82dBd28", amount: "2.34567", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Fund Manager role assigned. See Roles for more information.

The OAuth scope must have crypto:send assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/transfer-between-accounts.md # Transfer Between Accounts

Gemini Custody withdrawals occur during the daily custody run. Custody-to-Exchange transfers receive pre-credit. Custody accounts request withdrawals to approved addresses and require approved IP controls.

Custody accounts do not support fiat transfers. Fiat transfers between non-derivative and derivatives accounts are prohibited.

), }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/account/transfer/btc", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/account/transfer/btc", nonce: "", sourceAccount: "primary", targetAccount: "my-secondary-account", amount: "1.0", }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must be a Master level key with the Fund Manager role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/remove-approved-address.md # Remove Approved Address " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/approvedAddresses/ethereum/remove", nonce: "", address: "0x0000000000000000000000000000000000000000", }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Fund Manager role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/list-payment-methods.md # List Payment Methods " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/payments/methods", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

This endpoint can be accessed by either a Master or Account level key with any role assigned. See Roles for more information.

The OAuth scope must have banks:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/list-past-transfers.md # List Past Transfers

Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting. This endpoint is currently restricted further than standard rate limiting to a rate of 1 request per 5 seconds per subaccount. This rate is subject to change.

This endpoint does not currently show cancelled advances, returned outgoing wires or ACH transactions, or other exceptional transaction circumstances.

Fiat transfers between non-derivative and derivatives accounts are prohibited.

), }} example={{ request: { method: "POST", url: "https://api.gemini.com/v2/transfers", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v2/transfers", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned. See Roles for more information.

The OAuth scope must have history:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/list-deposit-addresses.md # List Deposit Addresses Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting. This endpoint is currently restricted further than standard rate limiting to a rate of 1 request per 2 seconds per subaccount. This rate is subject to change.

), }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/addresses/bitcoin", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/addresses/bitcoin", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned. See Roles for more information.

The OAuth scope must have addresses:read or addresses:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/list-custody-fee-transfers.md # List Custody Fee Transfers Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting. This endpoint is currently restricted further than standard rate limiting to a rate of 1 request per 5 seconds per subaccount — the same limit as the List Past Transfers endpoint. One call to one affects the other.

), }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/custodyaccountfees", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/custodyaccountfees", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned. See Roles for more information.

The OAuth scope must have history:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/list-approved-addresses.md # List Approved Addresses " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/approvedAddresses/account/ethereum", nonce: "", }, }, }} sections={[ { heading: "Roles", children: (

This endpoint can be accessed with any role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/get-transaction-history.md # Get Transaction History Due to current limitations, historical data can only be returned for dates after August 1st, 2022. Use continuation_token for pagination — do not use it together with timestamp_nanos.

), }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/transactions", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/transactions", nonce: "", limit: 100, }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned, with master account scope. See Roles for more information.

The OAuth scope must have history:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/get-notional-balances.md # Get Notional Balances

Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting.

Gemini is currently in the process of introducing new API architecture that will impact how decimal balances are returned from this endpoint for fiat and crypto assets.

As a result of this change, requests routed via the new architecture will return fiat balances and crypto balances truncated to 15 and 19 decimal places, respectively. It is recommended that users floor the values returned from this endpoint to the correct precision until the migration has been completed.

), }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/notionalbalances/usd", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/notionalbalances/usd", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned. See Roles for more information.

The OAuth scope must have balances:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/get-gas-fee-estimation.md # Get Gas Fee Estimation " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v2/withdraw/ethereum/eth/feeEstimate", nonce: "", address: "0xA63123350Acc8F5ee1b1fBd1A6717135e82dBd28", amount: "2.34567", account: "primary", }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Trader, Fund Manager, Auditor, WealthManager, or Administrator role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/get-available-balances.md # Get Available Balances

Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting.

Gemini is currently in the process of introducing new API architecture that will impact how decimal balances are returned from this endpoint for fiat and crypto assets.

As a result of this change, requests routed via the new architecture will return fiat balances and crypto balances truncated to 15 and 19 decimal places, respectively. It is recommended that users floor the values returned from this endpoint to the correct precision until the migration has been completed.

), }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/balances", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/balances", nonce: "", account: "primary", showPendingBalances: true, }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader, Fund Manager, or Auditor role assigned. See Roles for more information.

The OAuth scope must have balances:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/create-new-deposit-address.md # Create New Deposit Address Under the terms of the Gemini API Agreement, polling this endpoint may be subject to rate limiting. This endpoint is currently restricted further than standard rate limiting to a rate of 1 request per 2 seconds per subaccount. This rate is subject to change.

), }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/deposit/bitcoin/newAddress", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/deposit/bitcoin/newAddress", nonce: "", label: "optional test label", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Fund Manager role assigned. See Roles for more information.

The OAuth scope must have addresses:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/create-new-approved-address.md # Create New Approved Address " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/approvedAddresses/ethereum/request", nonce: "", address: "0x0000000000000000000000000000000000000000", label: "my-eth-address", }, }, }} sections={[ { heading: "Roles", children: (

The API key you use to access this endpoint must have the Fund Manager role assigned. See Roles for more information.

), }, { heading: "Headers", children: , }, { heading: "Path Parameters", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/add-bank.md # Add Bank " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/payments/addbank", nonce: "", accountnumber: "0011011100", routing: "026009593", type: "checking", name: "My Bank Account", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Fund Manager role assigned. See Roles for more information.

The OAuth scope must have banks:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/fund-management/add-bank-cad.md # Add Bank (CAD) " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/payments/addbank/cad", nonce: "", swiftcode: "ROYCCAT2", accountNumber: "0011011100", type: "checking", name: "My CAD Bank Account", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Fund Manager role assigned. See Roles for more information.

The OAuth scope must have banks:create assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/list-funding-payments.md # List Funding Payments The response field instrumentSymbol is only attached to requests from 16th April 2024 onwards.

}} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/perpetuals/fundingPayment", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/perpetuals/fundingPayment", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have orders:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Query Parameters", children: , }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/get-risk-stats.md # Get Risk Stats , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/get-open-positions.md # Get Open Positions " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/positions", nonce: "", account: "primary", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have orders:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/get-next-funding-timestamp.md # Get Next Funding Timestamp , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/get-funding-payment-report-json.md # Get Funding Payment Report JSON " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/perpetuals/fundingpaymentreport/records.json?fromDate=2024-04-10&toDate=2024-04-25&numRows=1000", nonce: "", }, }, }} sections={[ { heading: "Examples", children: (
  • fromDate=2024-04-10&toDate=2024-04-25&numRows=1000 — returns the lesser of the date-range record count and numRows.
  • fromDate=2024-04-10&toDate=2024-04-25 — returns all records in the date range.
  • numRows=1000 — returns up to 1000 records starting from now.
  • No parameters — returns the default maximum of 8760 records starting from now.
), }, { heading: "Query Parameters", children: , }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/get-funding-payment-report-file.md # Get Funding Payment Report File " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/perpetuals/fundingpaymentreport/records.xlsx", nonce: "", }, }, }} sections={[ { heading: "Roles", children: ( <>

The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

The OAuth scope must have orders:read assigned to access this endpoint. See OAuth Scopes for more information.

), }, { heading: "Examples", children: (
  • fromDate=2024-04-10&toDate=2024-04-25&numRows=1000 — returns the lesser of the date-range record count and numRows.
  • fromDate=2024-04-10&toDate=2024-04-25 — returns all records in the date range.
  • numRows=1000 — returns up to 1000 records starting from now.
  • No parameters — returns the default maximum of 8760 records starting from now.
), }, { heading: "Query Parameters", children: , }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/get-funding-amount.md # Get Funding Amount , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/get-funding-amount-report-file.md # Get Funding Amount Report File
  • symbol=BTCGUSDPERP&fromDate=2024-04-10&toDate=2024-04-25&numRows=1000 — returns the lesser of the date-range record count and numRows.
  • symbol=BTCGUSDPERP&fromDate=2024-04-10&toDate=2024-04-25 — returns all records in the date range.
  • symbol=BTCGUSDPERP&numRows=1000 — returns up to 1000 records starting from now.
  • symbol=BTCGUSDPERP — returns the default maximum of 8760 records starting from now.
  • ), }, { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/derivatives/get-account-margin.md # Get Account Margin " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/margin", nonce: "", symbol: "BTC-GUSD-PERP", }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

    The OAuth scope must have orders:read assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/clearing/list-clearing-trades.md # List Clearing Trades " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/clearing/trades", nonce: "", timestamp_nanos: 1630382206000000000, limit_per_account: 50, }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have clearing:read assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/clearing/list-clearing-orders.md # List Clearing Orders " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/clearing/list", nonce: "", symbol: "BTCEUR", side: "buy", }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have clearing:read assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/clearing/list-clearing-brokers.md # List Clearing Brokers " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/clearing/broker/list", nonce: "", symbol: "BTCEUR", }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have clearing:read assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/clearing/get-clearing-order.md # Get Clearing Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/clearing/status", nonce: "", clearing_id: "OM9VNL1G", }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have clearing:read assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/clearing/create-new-clearing-order.md # Create New Clearing Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/clearing/new", nonce: "", counterparty_id: "OM9VNL1G", expires_in_hrs: 24, symbol: "btcusd", amount: "100", price: "9500.00", side: "buy", }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have clearing:create assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/clearing/create-new-broker-order.md # Create New Broker Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/clearing/broker/new", nonce: "", source_counterparty_id: "R485E04Q", target_counterparty_id: "Z4929ZDY", symbol: "ethusd", amount: "175.00", expires_in_hrs: 1.0, price: "200", side: "sell", }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have clearing:create assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/clearing/confirm-clearing-order.md # Confirm Clearing Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/clearing/confirm", nonce: "", clearing_id: "OM9VNL1G", symbol: "btcusd", amount: "100", price: "9500.00", side: "sell", }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have clearing:create assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/rest-api/clearing/cancel-clearing-order.md # Cancel Clearing Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/clearing/cancel", nonce: "", clearing_id: "P0521QDV", }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have clearing:create assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/fix/overview/standard-trailer.md # Standard Trailer The [Standard Trailer](https://www.onixs.biz/fix-dictionary/4.4/compBlock_standard-trailer.html) is required on every message. --- ### FIELDS | Tag | Name | Req | Description | | :-: | ------------------------------------------------------------------- | :-: | -------------------------------------------------------------------- | | 10 | [CheckSum](https://www.onixs.biz/fix-dictionary/4.4/tagNum_10.html) | Y | Three-byte, simple checksum. Always the **last tag** in the message. | --- URL: https://developer.gemini.com/trading/fix/overview/standard-header.md # Standard Header The [Standard Header](https://www.onixs.biz/fix-dictionary/4.4/compBlock_standard-header.html) is required on every message. :::warning > **Note:** [PossResend `<97>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_97.html) is **not supported**. Gemini will report the last sequence number that the exchange received in the header of the [Logon ``](/trading/fix/overview/session-level-messages/logon) message. The client should assume that any events the server requests to be replayed have **not** been acted upon. (See [Beginning a Session]() for details.) ::: --- ### FIELDS | Tag | Name | Req | Description | | :-: | ---------------------------------------------------------------------------- | :-: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 8 | [BeginString](https://www.onixs.biz/fix-dictionary/4.4/tagNum_8.html) | Y | Identifies the beginning of a new message and protocol version. Always the **first** tag in the message.

    **Valid value**: `FIX.4.4` | | 9 | [BodyLength](https://www.onixs.biz/fix-dictionary/4.4/tagNum_9.html) | Y | Message length, in bytes, from right after this field up to (but **not** including) the [CheckSum `<10>`](https://www.onixs.biz/fix-dictionary/4.4/tagnum_10.html) field. Always the **second** tag in the message. | | 35 | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_35.html) | Y | Defines the message type. Always the **third** tag in the message. | | 34 | [MsgSeqNum](https://www.onixs.biz/fix-dictionary/4.4/tagNum_34.html) | Y | Sequence number of the message. | | 49 | [SenderCompID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_49.html) | Y | Assigned value used to identify the firm **sending** the message. | | 43 | [PossDupFlag](https://www.onixs.biz/fix-dictionary/4.4/tagNum_43.html) | N\* | Indicates **possible retransmission** of a message with this sequence number.

    **Valid values**:
    `Y` = Possible duplicate
    `N` = Original transmission

    \*Required for a re-transmitted message. | | 52 | [SendingTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_52.html) | Y | Time of message transmission (always in UTC). | | 56 | [TargetCompID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_56.html) | Y | Assigned value used to identify the firm **receiving** the message. | | 115 | [OnBehalfOfCompID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html) | N\* | Assigned value used to identify the **originating firm** if the message was delivered by a third party (e.g., an OMS or OEMS). **Required** if an OMS/OEMS is submitting/canceling orders on behalf of another Gemini account.

    Not used or supported in Market Data or single-party Order Entry or Drop Copy.

    **Third-party order entry** usage:
    • The customer's Gemini account identifier is used in [OnBehalfOfCompID `<115>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html).
    • The **third-party** firm identifier is used in [SenderCompID `<49>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_49.html).
    See [Order Entry: Third Party Support](/trading/fix/order-entry/third-party-support) for details.

    Third-party support for Drop Copy **does not** use [OnBehalfOfCompID `<115>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html) in the header. (See [Drop Copy: Third Party Support](/trading/fix/drop-copy/party-ids-&-roles/third-party-support) for details.) | | 128 | [DeliverToCompID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_128.html) | N\* | Used in **Execution Reports** only. Not used or supported in Market Data, single-party Order Entry, or Drop Copy.

    The customer's **Gemini account identifier** is returned in [DeliverToCompID `<128>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_128.html) on Execution Reports.

    Third-party support for Drop Copy **does not** use [DeliverToCompID `<128>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_128.html) in the header. (See [Drop Copy: Third Party Support](/trading/fix/drop-copy/party-ids-&-roles/third-party-support) for details.) | | 122 | [OrigSendingTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_122.html) | N\* | Original time of message transmission (in UTC) when transmitting orders as the result of a resend request. | > \* Required for a re-transmitted message. --- URL: https://developer.gemini.com/trading/fix/overview/precision-on-the-exchange.md # Precision on the exchange Quantity and price on incoming orders are strictly held to the minimums and increments shown in the [Supported Symbols Table](/market-data/symbols-and-minimums). However, once on the exchange, quantities and notional values may exhibit additional precision down to two decimal places past the listed “minimum order increment”. For example, it is possible for a `btcusd` trade to execute with a quantity of `0.0000000001 (1e-10)` BTC. This can happen due to: - Incoming market orders that may result in partial fills - Fees - Holds This additional precision is marketable once on the exchange. Your account balances are maintained with full fractional precision for each currency. --- URL: https://developer.gemini.com/trading/fix/overview/connecting.md # Connecting Gemini's primary trading platform and network point of presence (PoP) is housed in the Equinix NY5 data center in Secaucus, New Jersey. Customers can cross-connect from their NY2/NY4/NY5 infrastructure or leverage an approved extranet provider to access Gemini production Market Data feeds, Drop Copy, and Order Entry sessions. We are also nearing completion of a PoP in Equinix CH3 which will be available via cross connects from the Equinix family of data centers in Chicago and approved extranet providers. --- ## Steps to Get Connected to Our FIX Infrastructure 1. **Complete Account Verification** If your firm has not done so already, please complete the [Institutional Account Verification Process](https://exchange.gemini.com/register/institution/admin). 2. **Get Connected** Once on-boarded as a Gemini customer, email [connectivity@gemini.com](mailto:connectivity@gemini.com) with a brief description of your business and your preferred connectivity method. There are three options for production FIX connectivity to Gemini: - **Cross-connect** For customers with applications in the same data center as Gemini. If you would like to cross-connect, please include the legal entity name that we should include in our LOA (letter of authorization). - **Extranet Connectivity** Customers who are not in the Equinix family of data centers can leverage an extranet provider to connect to Gemini. If your firm wishes to leverage an extranet provider or third party to connect, please specify that in your email including the provider’s name. - **Third Party Order Management System Provider** There are a number of Service providers that allow Gemini customers to leverage their trading platforms to connect to Gemini for trading. If you would like to cross-connect, please include the legal entity name that we should include in our LOA (letter of authorization). If your firm wishes to leverage an extranet provider or third party to connect, please specify that in your email including the provider’s name. --- ## Sandbox and Testing Gemini operates a Sandbox environment which acts as a replica of our production environment. Gemini strongly recommends that all FIX customers create a Sandbox account to test their workflows. Please follow these steps to get a FIX Sandbox environment set up: 1. **Setup a Sandbox Account** Create a Sandbox account by going to https://sandbox.gemini.com. 2. **Permission Your Test IP** Email [connectivity@gemini.com](mailto:connectivity@gemini.com) and specify the email address used to set up your Sandbox account and the source IP that you will be using to connect to the FIX Sandbox. We will respond once the IP addresses have been enabled. We will also provide you with the Sender and Target CompID that should be used to connect to the test environment. 3. **Test Your Workflow Thoroughly** Test all messaging workflows specified in our FIX API documentation. (Gemini currently only supports [FIX 4.4](https://www.fixtrading.org/standards/fix-4-4/)) --- URL: https://developer.gemini.com/trading/fix/overview/allowed-characters.md # Allowed characters Client supplied FIX identifier field values must contain between at least one and up to one hundred allowed characters. Any exchange-bound message containing an invalid identifier will be rejected. Client supplied identifier values should match against this PCRE regular expression:
    `[:\-_\.#a-zA-Z0-9]{1,100}` --- ### CHARACTER TABLE | Characters | Description | ASCII Codes (Dec) | | :--------: | ----------------------------- | ----------------- | | `A-Z` | Uppercase letters A through Z | 65 – 90 | | `a-z` | Lowercase letters a through z | 97 – 122 | | `0-9` | Digits | 48 – 57 | | `#` | Hash, octothorpe, number sign | 35 | | `-` | Hyphen | 45 | | `.` | Period | 46 | | `:` | Colon | 58 | | `_` | Underscore | 95 | --- URL: https://developer.gemini.com/trading/fix/order-entry/third-party-support.md # Third Party Support Gemini allows OMS/OEMS firms to send orders on behalf of existing Gemini accounts after signing a **service bureau agreement**. Once the agreement is signed, **Gemini provides a unique CompID** for the account. The OMS/OEMS should use this CompID in the [OnBehalfOfCompID `<115>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html) field of the [header](/trading/fix/overview/standard-header): - The customer's Gemini account identifier is used in [OnBehalfOfCompID `<115>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html) - The third-party firm identifier is used in [SenderCompID `<49>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_49.html) --- ### Required Fields for Third-Party Messages The following messages require **both** [OnBehalfOfCompID `<115>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html) **and** [SenderCompID `<49>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_49.html): - [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit) for submitting orders on behalf of that account - [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request) for canceling orders on behalf of that account --- ### Execution Report Echo Fields When Gemini sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) in response to one of these messages: - It echoes back the [SenderCompID `<49>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_49.html) value in the header - The value originally in [OnBehalfOfCompID `<115>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html) is placed into [DeliverToCompID `<128>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_128.html) --- See [Drop Copy: Third Party Support](/trading/fix/drop-copy/party-ids-&-roles/third-party-support) for details on Drop Copy third party support. --- URL: https://developer.gemini.com/trading/fix/order-entry/introduction.md # Introduction Institutions can use the FIX Order Entry session to submit and cancel orders. --- URL: https://developer.gemini.com/trading/fix/order-entry/errors.md # Errors ### Error Responses When a message is received but cannot be properly processed due to some rule violation (like invalid field values or attributes), we will return back - a [Reject `<3>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_3_3.html), or - an [Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) with an [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) field with a value of `8 = Rejected` and an [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) field with a value of `8 = Rejected`, or - a [Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html) Where possible, there will be an error message in [Text `<58>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) explaining the Rejection. Here is a list of some common error messages and reasons for Rejection. ### Error Messages `](https://www.onixs.biz/fix-dictionary/4.4/msgType_3_3.html)", "Value is incorrect (out of range) for this tag", "Here are some reasons for why a field value may be invalid:\n\n \u00A0 \n\n• An order submitted on behalf of another party (identified by the [OnBehalfOfCompID `<115>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html) field) is submitted through a channel that does not offer Third Party Support.\n\n• An attempt was made to trade on behalf of someone without permission\n\n• Order's [TimeInForce `<59>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html) was invalid\n• Order quantity violates market minimums or increments for Symbol - see [Supported Symbols](/market-data/symbols-and-minimums)\n\n• Incompatible fields, such as a response to an IOI which also includes an [ExecInst `<18>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_18.html)", ], [ "[Reject `<3>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_3_3.html)", "Tag not defined for this message type (TAG)", "Message contains an extra unsupported [FIX tag](https://www.onixs.biz/fix-dictionary/4.4/fields_by_tag.html).", ], [ "[Reject `<3>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_3_3.html)", "Invalid tag value", "", ], [ "[Reject `<3>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_3_3.html)", "System Error: Message rate exceeded allocated throttle", "Throttle exceeded", ], [ "[Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) (Rejected)", "InvalidPrice", "The message specified a price that was too low or had the incorrect precision.", ], [ "[Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) (Rejected)", "Unsupported Symbol value 'ABCDEF'", "An invalid Symbol was provided (ex: 'ABCDEFGH' instead of 'BTCUSD' in [Symbol `<55>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html))", ], [ "[Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) (Rejected)", "StopPx is required for OrdType: 4", "New Stop Limit orders require a valid stop price in [StopPx `<99>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_99.html).", ], [ "[Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) (Rejected)", "Price must be within 50% of StopPx for OrdType: 4", "New Stop Limit orders require the Limit [Price `<44>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html) be within 50% of the [StopPx `<99>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_99.html).", ], [ "[Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) (Rejected)", "Stop limit orders only support standard order behavior", "If provided, `1 = Good Till Cancel (GTC)` is the only [TimeInForce `<59>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html) value allowed in conjunction with stop limit orders. All other values will trigger this rejection.", ], [ "[Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html)", "Unsupported message type", "Used to Reject a FIX message which is not unsupported by this specific FIX API, e.g. a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) received on a FIX Order Entry Channel.", ], [ "[Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html)", "Conditionally Required Field Missing (TAG)", "A conditionally required field, identified by its tag, is missing. (ex: [OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html) is missing)", ], [ "[Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html)", "System Error: Application not available", "Backend system not available", ], ]} /> --- URL: https://developer.gemini.com/trading/fix/market-data/introduction.md # Introduction These are the messages that can be sent and received for market data. For a reference for all FIX tags, see [FIXimate](https://fiximate.fixtrading.org/legacy/index.html). --- URL: https://developer.gemini.com/trading/fix/drop-copy/introduction.md # Introduction Institutions can use the FIX Drop Copy feed to receive confirmations of trades on the Gemini exchange. This will only report the trades themselves, not cancels or order placements. There are no client-originated messages supported on this channel except for the session-management ones. --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/websocket.md # TypeScript SDK — WebSocket Reference Every WebSocket stream and method, with the exact payload field names. WebSocket messages use a compact wire format with single-letter field names — this page maps each letter to its meaning so you never need to inspect the source. Public streams and controls are on `client.websocket.public`; authenticated streams and controls are on `client.websocket.private` in the server entry point. These are separate WebSocket connections: public frames never enter the authenticated session, and private operations are not available through the browser entry point. See the [WebSocket guide](/tools/typescript-sdk/websocket) for lifecycle, reconnection, and usage patterns. ## Public streams No authentication required. Each returns a `WebSocketStream` you subscribe to with `.on("message", …)`. | Method | Payload type | Description | | --- | --- | --- | | `public.trades(symbol, options?)` | `Trade` | Real-time trade prints | | `public.bookTicker(symbol, options?)` | `BookTicker` | Best bid/ask updates | | `public.depthUpdates(symbol, options?)` | `DepthUpdate` | Incremental order book diffs | | `public.depth(symbol, options)` | `OrderBookSnapshot` | Periodic top-N depth snapshots (`options.levels`: 5, 10, or 20) | | `public.contractStatus(options?)` | `ContractStatus` | Prediction market contract status changes | | `public.rfqs(options?)` | `RfqPublicEvent` | Public request-for-quote events (production beta; no authentication required) | ## Authenticated streams Require an `auth` strategy and the server entry point (browser WebSocket cannot set upgrade headers). | Method | Payload type | Description | | --- | --- | --- | | `private.orders(options)` | `OrderUpdate` | Order lifecycle updates (`options.scope`: `"account"` or `"session"`) | | `private.balances(options?)` | `BalanceUpdate` | Balance changes (`options.intervalMs`: 0 or 1000) | | `private.positions(options?)` | `PositionReport` | Position updates (`options.intervalMs`: 0 or 1000); terminal prediction-market settlement rows are available on the realtime stream | | `private.rfqDeliveries(options)` | `RfqPrivateDelivery` | Private RFQ delivery confirmations (`options.scope`) (production beta; quoting requires an eligible account) | `intervalMs: 1000` selects `positions@account@1s`, a periodic snapshot of open positions. It does not include terminal settlement rows. ## Request/response methods These send a request and resolve with a response, rather than streaming. | Method | Returns | Description | | --- | --- | --- | | `private.placeOrder(params, options?)` | `OrderActionResponse` | Place an order over WebSocket | | `private.cancelOrder(params, options?)` | `OrderActionResponse` | Cancel a single order | | `private.cancelAllOrders(options)` | `OrderActionResponse` | Cancel all orders (`options.confirm` must be `true`) | | `private.cancelSessionOrders(options)` | `OrderActionResponse` | Cancel session orders (`options.confirm` must be `true`) | | `public.ping(options?)` | `GenericSuccessResponse` | Round-trip liveness check on the public connection | | `public.time(options?)` | `GenericSuccessResponse` | Server time from the public connection | | `public.conninfo(options?)` | `WebSocketJsonObject` | Connection info for the public connection | | `public.listSubscriptions(options?)` | `ListSubscriptionsResponse` | Subscriptions on the public connection | | `public.depthSnapshot(symbol, options?)` | `DepthResponse` | One-shot depth snapshot | | `private.conninfo(options?)` | `WebSocketJsonObject` | Connection info for the authenticated private connection | | `private.listSubscriptions(options?)` | `ListSubscriptionsResponse` | Subscriptions on the authenticated private connection | ### RFQ quote methods > **Availability:** RFQ streams and methods are available in production for beta testing. Quoting requires an eligible account with the required capabilities. Accessed via `client.websocket.private.rfq` on server clients: | Method | Params | Returns | | --- | --- | --- | | `private.rfq.submitQuote(params, options?)` | `RfqSubmitQuoteParams` | `RfqSubmitQuoteResponse` | | `private.rfq.withdrawQuote(params, options?)` | `RfqWithdrawQuoteParams` | `RfqWithdrawQuoteResponse` | | `private.rfq.confirmQuote(params, options?)` | `RfqConfirmQuoteParams` | `RfqConfirmQuoteResponse` | ## Wire format WebSocket payloads use single-letter field names. These are the exact fields on each type. Prices and quantities are **decimal strings** (never floats — see [Data Types](/tools/typescript-sdk/deep-dives/data-types)); timestamps and IDs may be `bigint`. ### Trade ```ts const trades = client.websocket.public.trades("BTCUSD"); trades.on("message", (t) => console.log(t.p, t.q, t.m)); ``` | Field | Type | Meaning | | --- | --- | --- | | `E` | `number \| bigint` | Event time (nanoseconds) | | `s` | `string` | Symbol | | `t` | `number \| bigint` | Trade ID | | `p` | `string` | Price | | `q` | `string` | Quantity | | `m` | `boolean` | Whether the buyer is the maker | ### BookTicker | Field | Type | Meaning | | --- | --- | --- | | `u` | `number \| bigint` | Update ID | | `E` | `number \| bigint` | Event time (nanoseconds) | | `s` | `string` | Symbol | | `b` | `string` | Best bid price | | `B` | `string` | Best bid quantity | | `a` | `string` | Best ask price | | `A` | `string` | Best ask quantity | | `c` | `string?` | Last trade price (present once the book has traded) | | `C` | `string?` | Last trade quantity | ### DepthUpdate | Field | Type | Meaning | | --- | --- | --- | | `e` | `"depthUpdate"` | Event type discriminator | | `E` | `number \| bigint` | Event time (nanoseconds) | | `s` | `string` | Symbol | | `U` | `number \| bigint` | First update ID in this diff | | `u` | `number \| bigint` | Last update ID in this diff | | `b` | `string[][]` | Bid changes as `[price, quantity]` pairs | | `a` | `string[][]` | Ask changes as `[price, quantity]` pairs | A quantity of `"0"` means the level was removed. See [Order Book Reconstruction](/tools/typescript-sdk/deep-dives/order-book) for how the SDK applies these. ### OrderUpdate ```ts const orders = client.websocket.private.orders({ scope: "session" }); orders.on("message", (o) => console.log(o.i, o.X, o.z)); ``` | Field | Type | Meaning | | --- | --- | --- | | `e` | `"orderUpdate"` | Event type discriminator | | `E` | `number \| bigint` | Event time (nanoseconds) | | `s` | `string` | Symbol | | `i` | `number \| bigint` | Order ID | | `c` | `string?` | Client order ID. For RFQ maker fills, this is the `clientId` supplied to `rfq.submit_quote`, or Gemini's deterministic RFQ client order ID when omitted. | | `S` | `"BUY" \| "SELL"` (optional) | Side | | `o` | `"LIMIT" \| "MARKET" \| "STOP_LIMIT" \| "STOP_MARKET"` (optional) | Order type | | `X` | `"NEW" \| "OPEN" \| "FILLED" \| "PARTIALLY_FILLED" \| "CANCELED" \| "REJECTED" \| "MODIFIED"` | Order status | | `O` | `"YES" \| "NO"` (optional) | Prediction outcome | | `p` | `string?` | Order price | | `P` | `string?` | Stop price | | `q` | `string?` | Order quantity | | `z` | `string?` | Remaining quantity | | `Z` | `string?` | Executed quantity (last fill for FILLED/PARTIALLY_FILLED; cumulative for CANCELED and other terminal events) | | `L` | `string?` | Last fill price | | `t` | `number \| bigint` (optional) | Trade ID of the last fill | | `n` | `string?` | Commission | | `m` | `boolean?` | Whether this order was the maker | | `r` | `string?` | Reject reason | | `T` | `number \| bigint` | Transaction time (nanoseconds) | ### BalanceUpdate ```ts const balances = client.websocket.private.balances(); balances.on("message", (u) => { for (const b of u.B) console.log(b.a, b.f, b.c); }); ``` | Field | Type | Meaning | | --- | --- | --- | | `e` | `"balanceUpdate"` | Event type discriminator | | `E` | `number \| bigint` | Event time (nanoseconds) | | `u` | `number \| bigint` | Update ID | | `B` | `Balance[]` | Balance entries | Each `Balance`: | Field | Type | Meaning | | --- | --- | --- | | `a` | `string` | Asset | | `f` | `string` | Free (available) balance | | `c` | `string` | Locked balance | ### PositionReport | Field | Type | Meaning | | --- | --- | --- | | `e` | `"positionReport"` | Event type discriminator | | `E` | `number \| bigint` | Event time (nanoseconds) | | `u` | `number \| bigint` | Last account-update timestamp (nanoseconds) | | `A` | `number \| bigint` | Account reference | | `P` | `PositionRow[]` | Position entries | Each `PositionRow`: | Field | Type | Meaning | | --- | --- | --- | | `t` | `string` | Type | | `s` | `string` | Symbol | | `a` | `NamedAmount[]` | Named amounts for the position | Each `NamedAmount`: | Field | Type | Meaning | | --- | --- | --- | | `t` | `string` | Amount label, such as `position` or `settlement_payout` | | `v` | `string` | Decimal amount; position quantities are signed | | `c` | `string?` | Optional asset code, such as `usd` for settlement payouts | | `o` | `("YES" \| "NO" \| "UNSPECIFIED")?` | Settlement outcome on `settlement_payout` amounts | For a settled event-contract position, the terminal row includes a `position` amount with value `"0"` and a `settlement_payout` amount. The payout amount uses `c: "usd"` and includes `o: "YES"`, `"NO"`, or `"UNSPECIFIED"`. Terminal settlement rows are delivered only by the realtime `positions@account` stream. The one-second `positions@account@1s` stream reports open-position snapshots and omits those terminal rows. ### ContractStatus | Field | Type | Meaning | | --- | --- | --- | | `e` | `"contractStatus"` | Event type discriminator | | `E` | `number \| bigint` | Event time (milliseconds) | | `s` | `string` | Symbol | | `k` | `string` | Event ticker | | `c` | `string` | Contract ticker | | `i` | `number \| bigint` | Contract ID | | `p` | `string?` | Price | | `o` | `string` | Previous status | | `n` | `string` | New status | ## What's next - [Order Book Reconstruction](/tools/typescript-sdk/deep-dives/order-book) — how depth diffs become a live book - [RFQ Protocol](/tools/typescript-sdk/deep-dives/rfq) — the request-for-quote maker flow - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — why timestamps are `bigint` and prices are strings --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/perpetuals.md # TypeScript SDK — Perpetuals Reference Methods for perpetual-contract positions, margin, risk statistics, and funding payment history. All methods are on `client.perpetuals`. See the [API Specifications](/api-specifications) for full request/response schemas. ## Positions & Margin ### getAccountMargin `POST /v1/margin` · Authenticated Retrieve the perpetuals margin summary for a symbol — margin assets value, initial margin, available margin, and liquidation information. ```ts const margin = await client.perpetuals.getAccountMargin({ symbol: "BTC-GUSD-PERP", }); console.log(margin.margin_assets_value); // account margin asset value (decimal string) console.log(margin.initial_margin); // margin in use (decimal string) console.log(margin.available_margin); // available margin (decimal string) console.log(margin.leverage); // leverage ratio (decimal string) console.log(margin.buying_power); // buying power (decimal string) ``` > **Note:** This is a POST mutation and is **not** automatically retried. > **Tip:** All margin values are **decimal strings**. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### getOpenPositions `POST /v1/positions` · Authenticated Retrieve all open perpetual-contract positions on the account. The response wraps the positions array in an `openPositions` field. ```ts const result = await client.perpetuals.getOpenPositions({}); for (const pos of result.openPositions ?? []) { console.log(pos.symbol); // e.g. "btcgusdperp" console.log(pos.quantity); // position size (decimal string, negative for shorts) console.log(pos.average_cost); // average entry price (decimal string) console.log(pos.unrealised_pnl); // unrealized P&L (decimal string) console.log(pos.mark_price); // current mark price (decimal string) } ``` > **Note:** This is a POST mutation and is **not** automatically retried. ### getRiskStats `GET /v1/riskstats/{symbol}` · Public Retrieve risk statistics for a specific perpetual symbol — mark price, index price, and open interest. ```ts const stats = await client.perpetuals.getRiskStats({ symbol: "BTCGUSDPERP" }); console.log(stats.product_type); // "PerpetualSwapContract" console.log(stats.mark_price); // current mark price (decimal string) console.log(stats.index_price); // index price (decimal string) console.log(stats.open_interest); // open interest (decimal string) console.log(stats.open_interest_notional); // open interest notional (decimal string) ``` > **Auto-retried.** This is a GET endpoint — the SDK automatically retries on `429`, `502`, `503`, and `504`. > **Public endpoint.** No authentication required. The `symbol` path parameter is passed as a top-level field. ## Funding ### listFundingPayments `POST /v1/perpetuals/fundingPayment` · Authenticated Retrieve funding payments for your perpetual positions. Supports optional `since` and `to` query parameters to filter by time range. Each payment wraps a `hourlyFundingTransfer` object with the transfer details. ```ts const payments = await client.perpetuals.listFundingPayments({ since: 1700000000000n, to: 1700100000000n, }); for (const payment of payments) { console.log(payment.eventType); // "Hourly Funding Transfer" const transfer = payment.hourlyFundingTransfer; console.log(transfer.assetCode); // e.g. "GUSD" console.log(transfer.action); // "Credit" or "Debit" console.log(transfer.quantity.currency); // e.g. "GUSD" console.log(transfer.quantity.value); // funding amount (decimal string) console.log(transfer.instrumentSymbol); // e.g. "BTCGUSDPERP" } ``` > **Note:** This is a POST mutation and is **not** automatically retried. > **Input pattern.** Parameters are passed in a flat object. The `since` and `to` query parameters are int64 timestamps — the SDK accepts both `bigint` and `number`. ### getFundingPaymentReportFile `GET /v1/perpetuals/fundingpaymentreport/records.xlsx` · Authenticated Download a funding payment report as an Excel spreadsheet file. Returns raw bytes, **not JSON**. ```ts const report = await client.perpetuals.getFundingPaymentReportFile({ fromDate: "2024-01-01", toDate: "2024-01-31", numRows: 100, }); // report.bytes is a Uint8Array containing the .xlsx file // report.contentType is "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" // report.contentDisposition contains the suggested filename import { writeFile } from "node:fs/promises"; await writeFile("funding-report.xlsx", report.bytes); ``` > **Returns a file, not JSON.** The response is a `RestFileResponse` with `bytes: Uint8Array`, `contentType?: string`, and `contentDisposition?: string`. > **Auto-retried.** This is a GET endpoint — the SDK automatically retries on transient errors. > **All query parameters are optional.** Omit them or pass `{}` to get a default report. ### getFundingPaymentReportJson `POST /v1/perpetuals/fundingpaymentreport/records.json` · Authenticated Retrieve funding payment report data as JSON. Same data as `getFundingPaymentReportFile` but in a structured JSON format. ```ts const report = await client.perpetuals.getFundingPaymentReportJson({ fromDate: "2024-01-01", toDate: "2024-01-31", }); for (const record of report) { console.log(record.eventType); // "Hourly Funding Transfer" console.log(record.assetCode); // e.g. "GUSD" console.log(record.action); // "Credit" or "Debit" console.log(record.quantity.currency); // e.g. "GUSD" console.log(record.quantity.value); // funding amount (decimal string) console.log(record.instrumentSymbol); // e.g. "BTCGUSDPERP" } ``` > **Note:** This is a POST mutation and is **not** automatically retried. > **Input pattern.** This method takes `query` (optional) and `body` keys. ## What's next - [Margin Reference](/tools/typescript-sdk/reference/margin) — margin account details and borrow rates (separate from perpetuals margin) - [Market Data: Networks & Derivatives](/tools/typescript-sdk/reference/market-data/networks-and-derivatives) — public funding-amount data - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and bigint handling - [Error Handling](/tools/typescript-sdk/errors) — error types and metadata --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/overview.md # TypeScript SDK — REST API Reference Complete reference for every REST namespace on the client returned by `createClient()`. Each namespace groups related operations and is accessed as a property on the client instance. ## Namespace map | Accessor | Operations | Description | | --- | ---: | --- | | `client.marketData` | 16 | Public and authenticated market data — tickers, order books, candles, symbols, trades, fee promos, next funding timestamps, and funding-amount reports | | `client.trading` | 12 | Order lifecycle, active/past order queries, trade history, volume stats, session heartbeat, and wrapped orders | | `client.predictions` | 31 | Prediction market events, orders, positions, combos, volume, liquidity rewards, and maker rebates | | `client.perpetuals` | 6 | Perpetual-contract positions, margin, risk stats, and funding payment history | | `client.margin` | 3 | Margin account details, borrow rates, and order previews | | `client.clearing` | 8 | OTC clearing workflows, counterparty orders, broker orders, and clearing trade history | | `client.instant` | 2 | Instant-execution quote requests and order executions | | `client.account` | 16 | Balances, account details, roles, subaccounts, banking, deposit and approved-address management, and OAuth revocation | | `client.staking` | 6 | Staking balances, rates, rewards, event history, stake, and unstake | | `client.transfers` | 6 | Withdrawals, internal transfers, transfer and transaction history, custody-fee transfers, and gas-fee estimates | | **Total** | **106** | | ## Conventions ### 100% Flat Unified Parameter Objects All SDK methods accept a single flat parameter object containing all path parameters, query parameters, and request body fields combined: ```ts // Path parameter (symbol) in flat object const ticker = await client.marketData.getTicker({ symbol: "BTCUSD" }); // Path parameter (symbol) + body fields (amount, side) all in one flat object const result = await client.trading.wrapOrder({ symbol: "BTCUSD", amount: "1.0", side: "buy", }); ``` You never need nested `path`, `query`, or `body` wrapper objects. All parameters are flat, fully typed, and validated. ### Transport fields are handled automatically You never pass `nonce` or `request` — the SDK injects transport authentication parameters automatically for authenticated requests. ### RequestOptions Every method accepts an optional `RequestOptions` parameter as the last argument: ```ts interface RequestOptions { signal?: AbortSignal; // cancel with an AbortController timeoutMs?: number; // per-request deadline in milliseconds } const order = await client.trading.getOrderStatus( { order_id: 12345n }, { timeoutMs: 5_000 }, ); ``` ### Retry policy - **GET operations** (public or authenticated) automatically retry on `429`, `502`, `503`, and `504` with exponential backoff. - **POST mutations** are **never retried** by the SDK — a failed mutation could have been applied server-side. Handle retries in your own code if idempotency is guaranteed. ### Client-side validation Some operations validate the request body locally before sending. If validation fails, the SDK throws a `ValidationError` synchronously — no network request is made. Validated methods are marked on their individual reference pages. See the [Request Validation deep dive](/tools/typescript-sdk/deep-dives/request-validation) for details. ### Prices and quantities are strings Most prices, amounts, and quantities in both requests and responses are **decimal strings** (e.g. `"50000.00"`), never floating-point numbers. However, some fields use plain `number` — notably `Balance.amount`, `Balance.available`, `FxRate.rate`, and candle OHLCV values. Some IDs and timestamps are `bigint`. See [Data Types](/tools/typescript-sdk/deep-dives/data-types) for the complete list. ### Full request/response schemas This reference documents method signatures, HTTP details, and usage patterns. For the complete request and response JSON schemas, see the [API Specifications](/api-specifications). ## Reference pages - **Market Data** — [Symbols & Pricing](/tools/typescript-sdk/reference/market-data/symbols-and-pricing) · [Books, Trades & Candles](/tools/typescript-sdk/reference/market-data/books-trades-candles) · [Networks & Derivatives](/tools/typescript-sdk/reference/market-data/networks-and-derivatives) - **Trading** — [Order Lifecycle](/tools/typescript-sdk/reference/trading/order-lifecycle) · [History & Volume](/tools/typescript-sdk/reference/trading/history-and-volume) - **Predictions** — [Events & Discovery](/tools/typescript-sdk/reference/predictions/events-and-discovery) · [Order Management](/tools/typescript-sdk/reference/predictions/order-management) · [Positions & Terms](/tools/typescript-sdk/reference/predictions/positions-and-terms) · [Combos](/tools/typescript-sdk/reference/predictions/combos) · [Volume & Metrics](/tools/typescript-sdk/reference/predictions/volume-and-metrics) · [Rewards & Rebates](/tools/typescript-sdk/reference/predictions/rewards-and-rebates) - **Perpetuals** — [Perpetuals](/tools/typescript-sdk/reference/perpetuals) - **Margin** — [Margin](/tools/typescript-sdk/reference/margin) - **Clearing** — [Clearing Orders](/tools/typescript-sdk/reference/clearing/clearing-orders) · [Instant Orders](/tools/typescript-sdk/reference/clearing/instant-orders) - **Account** — [Balances & Account](/tools/typescript-sdk/reference/account-services/balances-and-account) · [Addresses & Deposits](/tools/typescript-sdk/reference/account-services/addresses-and-deposits) · [Banking](/tools/typescript-sdk/reference/account-services/banking) · [OAuth](/tools/typescript-sdk/reference/account-services/oauth) - **Staking** — [Staking](/tools/typescript-sdk/reference/account-services/staking) - **Transfers** — [Withdrawals & Transfers](/tools/typescript-sdk/reference/account-services/withdrawals-and-transfers) - **WebSocket** — [WebSocket Reference](/tools/typescript-sdk/reference/websocket) ## Related guides - [Authentication](/tools/typescript-sdk/authentication) — API key setup and auth strategies - [Error Handling](/tools/typescript-sdk/errors) — error types, retry guidance, and error metadata - [Patterns](/tools/typescript-sdk/patterns) — pagination, streaming, and common workflows --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/margin.md # TypeScript SDK — Margin Reference Methods for querying margin account status, borrow rates, and previewing margin orders. All methods are on `client.margin`. Every method in this namespace is a **POST mutation** — the SDK will **not** automatically retry on failure. See the [API Specifications](/api-specifications) for full request/response schemas. ### getMarginAccount `POST /v1/margin/account` · Authenticated Retrieve your margin account summary — collateral, leverage, buying/selling power, and liquidation risk. The response is a `MarginAccountSummary` where monetary fields are `MoneyAmount` objects with `currency` and `value` properties. Requires the **Trader**, **Fund Manager**, or **Auditor** role. OAuth scope: `balances:read`. For Master API keys, include the `account` field in the request body to target a specific sub-account. ```ts const account = await client.margin.getMarginAccount({}); console.log(account.marginAssetValue.value); // total margin asset value (decimal string) console.log(account.marginAssetValue.currency); // e.g. "USD" console.log(account.availableCollateral.value); // available collateral (decimal string) console.log(account.leverage); // leverage ratio (decimal string) console.log(account.buyingPower.value); // buying power (decimal string) console.log(account.sellingPower.value); // selling power (decimal string) ``` > **Tip:** Monetary fields (`marginAssetValue`, `availableCollateral`, `notionalValue`, `totalBorrowed`, `buyingPower`, `sellingPower`, `reservedBuyOrders`, `reservedSellOrders`) are `MoneyAmount` objects with `.currency` and `.value` (decimal string). The `leverage` field is a plain decimal string. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### getMarginRates `POST /v1/margin/rates` · Authenticated Retrieve current margin borrow rates for all eligible currencies. Requires the **Trader**, **Fund Manager**, or **Auditor** role. OAuth scope: `balances:read`. ```ts const result = await client.margin.getMarginRates({}); for (const rate of result.rates) { console.log(rate.currency); // e.g. "BTC", "USD" console.log(rate.borrowRate); // hourly borrow rate (decimal string) console.log(rate.borrowRateDaily); // daily borrow rate (decimal string) console.log(rate.borrowRateAnnual); // annual borrow rate (decimal string) console.log(rate.lastUpdated); // timestamp (bigint) } ``` > **Caveat:** The `lastUpdated` field in each rate entry is a `bigint` timestamp. The SDK deserializes it automatically. > **Tip:** Borrow rates are **decimal strings**. Three granularities are provided: hourly (`borrowRate`), daily (`borrowRateDaily`), and annual (`borrowRateAnnual`). ### previewMarginOrder `POST /v1/margin/order/preview` · Authenticated Preview the margin impact of a hypothetical order before placing it. Returns pre-order and post-order risk statistics as `MarginRiskStats` objects, allowing you to see how the order would affect your margin account. ```ts const preview = await client.margin.previewMarginOrder({ symbol: "btcusd", amount: "0.5", price: "50000.00", side: "buy", type: "limit", }); // Before the order console.log(preview.preorder.marginAssetValue.value); // current margin asset value console.log(preview.preorder.availableCollateral.value); // current available collateral console.log(preview.preorder.leverage); // current leverage // After the order (projected) console.log(preview.postorder.marginAssetValue.value); // projected margin asset value console.log(preview.postorder.availableCollateral.value); // projected available collateral console.log(preview.postorder.leverage); // projected leverage ``` > **Tip:** Use this before `client.trading.createNewOrder()` to understand the margin impact of a trade. The preview does not place any order or reserve any margin. > **Tip:** Both `preorder` and `postorder` are `MarginRiskStats` objects. While they share the same monetary field pattern (`MoneyAmount` objects with `.currency` and `.value`), `MarginRiskStats` is a distinct type from `MarginAccountSummary` — the set of fields may differ. Check the [API Specifications](/api-specifications) for the exact schema. ## What's next - [Perpetuals Reference](/tools/typescript-sdk/reference/perpetuals) — perpetual-contract margin and positions (separate margin system) - [Trading: Order Lifecycle](/tools/typescript-sdk/reference/trading/order-lifecycle) — place orders after previewing - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and bigint handling - [Error Handling](/tools/typescript-sdk/errors) — error types and metadata --- URL: https://developer.gemini.com/tools/typescript-sdk/deep-dives/websocket-sessions.md # Deep Dive — WebSocket Sessions The TypeScript SDK manages WebSocket connections, reconnection, and subscription lifecycles automatically. This guide details session architectures, connection counts, and lifecycle management. ## Session architecture Every WebSocket stream opened via `client.websocket.public` or `client.websocket.private` attaches to a `WebSocketSession`. Sessions manage socket lifecycles, message routing, and subscription replay across three categories: ### Shared sessions Public streams share one underlying `WebSocketSession`; authenticated streams share a separate authenticated `WebSocketSession`. This prevents public frames and connection state from crossing the authentication boundary: - `public.trades(symbol)` — public trade stream - `public.bookTicker(symbol)` — public best bid/ask - `public.depthUpdates(symbol)` — public depth diffs - `public.contractStatus()` — perpetual contract status - `public.rfqs()` — public RFQ feed - `private.orders({ scope })` — authenticated order updates (`scope` is `"account"` or `"session"`) - `private.balances(options)` — authenticated balance updates - `private.positions(options)` — authenticated position reports - `private.rfqDeliveries({ scope })` — authenticated RFQ delivery events (`scope` is `"account"` or `"session"`) Each session is created lazily on its first stream request and reused for subsequent streams in that category. ### Isolated sessions Partial-depth snapshot streams (`depth()`) each get their **own** `WebSocketSession`. This is because they connect to a different snapshot URL and their data semantics require a dedicated connection: ```ts // Each call creates a separate WebSocket connection const depth5 = client.websocket.public.depth("BTCUSD", { levels: 5 }); const depth20 = client.websocket.public.depth("ETHUSD", { levels: 20 }); // That's 2 separate WebSocket connections, plus any public and authenticated shared sessions ``` When the stream is closed, its isolated session is torn down. ### Order book session `orderBook()` uses a **separate** shared session dedicated to order book management. All order book subscriptions multiplex over this one connection, but it is distinct from the public and authenticated sessions: ```ts const btcBook = client.websocket.public.orderBook("BTCUSD"); const ethBook = client.websocket.public.orderBook("ETHUSD"); // Both share the same book session (1 connection), separate from the main session ``` The book session handles snapshot synchronization, diff application, and automatic resync — see the [Order Book deep dive](/tools/typescript-sdk/deep-dives/order-book) for details. ### Connection count summary | Usage pattern | Connections | |---|---| | 3 public streams | 1 (shared) | | 3 public + 2 authenticated streams | 2 (public + authenticated) | | 2 order books | 1 (book) | | 1 public stream + 1 `depth()` + 1 order book | 3 (shared + isolated + book) | ## Reconnect behavior When a WebSocket connection drops, `WebSocketSession` reconnects automatically with exponential backoff. ### Backoff parameters WebSocket reconnect backoff uses equal jitter with these defaults. The policy is configurable through `webSocketReconnect`; the `backoff` client option continues to apply only to HTTP retries: | Parameter | Value | Notes | |---|---|---| | Base delay | 250 ms | Configurable through `webSocketBackoff.baseMs` | | Cap | 30 s | Configurable through `webSocketBackoff.capMs` | | Factor | 2 | Configurable through `webSocketBackoff.factor` | | Jitter | Equal jitter | Half fixed + half random | | Retry attempts | 10 | Default maximum after a connection drop | | Stable uptime | 30 s | Backoff resets only after this continuous uptime | Attempt 0 reconnects **immediately** (delay = 0) — most drops are transient. Subsequent attempts grow exponentially: 250 ms → 500 ms → 1 s → ... capped at 30 s. Equal jitter prevents many clients from reconnecting in lockstep after an exchange restart. A connection that flaps before stable uptime does not reset its retry budget. Configure the public client policy when an application needs a different bounded retry budget or a close-code classifier: ```ts const client = await createClient({ env: "sandbox", webSocketReconnect: { maxAttempts: 5, stableConnectionMs: 30_000, shouldReconnect: ({ closeCode }) => closeCode !== 1008, }, }); ``` Set `unlimited: true` only when the application deliberately owns the lifecycle of an indefinitely reconnecting stream. Authentication, policy, and unsupported-protocol close codes are treated as terminal by default. A connection timeout also cancels pending credential generation and fences the underlying socket attempt. ### What happens on reconnect 1. The socket closes (unexpectedly — not by `close()`) 2. `WebSocketSession` schedules a reconnect after the backoff delay 3. If the session has authentication, fresh auth headers are generated via `headersFactory` 4. A new socket is opened; stale sockets are ignored (late events from a superseded socket are discarded) 5. On open, all durable subscriptions are replayed 6. Streams transition: `"active"` → `"reconnecting"` → `"active"` If the reconnect policy is exhausted or a terminal close code is received, active streams transition to `"failed"` and expose the terminal error through `lastError`; they do not remain indefinitely in `"reconnecting"`. ### Pending requests reject on reconnect One-shot request/response methods (`public.ping()`, `public.time()`, `public.conninfo()`, `public.listSubscriptions()`, `public.depthSnapshot()`) that are in-flight when a reconnect occurs are **rejected** immediately. They are not replayed — you must retry them yourself: ```ts try { const pong = await client.websocket.public.ping(); } catch (err) { // If the connection dropped mid-request, this rejects with: // "WebSocket session reconnecting" } ``` ### Public and private connection state Public and authenticated subscriptions use separate WebSocket connections. Connection-scoped controls are explicit: `client.websocket.public` owns the unauthenticated session, and `client.websocket.private` owns the authenticated server session. ```ts const publicSubscriptions = await client.websocket.public.listSubscriptions(); const privateSubscriptions = await client.websocket.private.listSubscriptions(); ``` ### Mutations NEVER replay Order placement, cancellation, and RFQ operations (`private.placeOrder()`, `private.cancelOrder()`, `private.cancelAllOrders()`, `private.cancelSessionOrders()`, `private.rfq.submitQuote()`, etc.) are classified as mutations. They are one-shot: rejected on disconnect, never retried or replayed. This is a safety guarantee — replaying a mutating request after reconnect could cause duplicate fills. ## Subscription replay Every `SUBSCRIBE` frame is stored by the transport. On reconnect, all stored subscriptions are re-sent to the fresh socket, restoring your streams without manual intervention. ### Replay lifecycle ``` socket closes → backoff delay → new socket opens → replay all subscriptions → await ACK for each → "resubscribed" event fires per stream ``` Listen for replay events on individual streams: ```ts const trades = client.websocket.public.trades("BTCUSD"); trades.on("resubscribed", () => { console.log("Subscription restored after reconnect"); }); trades.on("subscriptionError", (err) => { console.error("Resubscription failed:", err); // The stream state is now "failed" }); ``` ### Stream states A stream's `.state` property reflects its lifecycle: | State | Meaning | |---|---| | `"active"` | Connected and receiving data | | `"reconnecting"` | Connection dropped; waiting for reconnect and resubscription | | `"failed"` | An error occurred (check `.lastError`) | | `"closed"` | Explicitly closed by the caller | ## Liveness checks Long-lived WebSocket connections can go stale without a TCP close event (half-open connections, NAT timeouts, silent proxy drops). The SDK detects this with application-level ping/pong liveness checks. ### Configuration Pass `webSocketLiveness` when creating the client: ```ts import { createClient, HmacAuth } from "@gemini-markets/sdk/server"; const client = await createClient({ env: "sandbox", // ... webSocketLiveness: { intervalMs: 30_000, // ping every 30 seconds (default) timeoutMs: 10_000, // expect pong within 10 seconds (default) }, }); ``` ### How it works 1. After each successful connection or ping response, a timer is scheduled at `intervalMs` 2. When the timer fires, the session sends a `ping` request over the WebSocket 3. If no response arrives within `timeoutMs`, the connection is considered dead 4. The session forces a reconnect (triggering the normal reconnect/replay flow) Without liveness configured, the SDK relies on the operating system's TCP keepalive and the exchange's server-side timeouts to detect dead connections. ## Frame size limits The SDK enforces a maximum inbound WebSocket message size to prevent memory exhaustion from malformed or malicious frames: ```ts const client = await createClient({ env: "sandbox", // ... webSocketMaxMessageSizeBytes: 1_048_576, // 1 MB (default) }); ``` Frames exceeding this limit are rejected: the SDK emits a `ConnectionError`, fires the `error` event on the transport, and closes the socket (which triggers a reconnect). The rejected frame is never delivered to stream listeners. The size check uses UTF-8 byte length, not JavaScript string `.length`. ## Diagnostic traffic classification Every WebSocket diagnostic event is classified by traffic type for structured logging and observability: | Classification | Events | |---|---| | `control` | SUBSCRIBE, UNSUBSCRIBE, connection open/close, socket factory errors | | `stream` | Data frames (trades, depth updates, book tickers), malformed frames | | `reconnect` | Connection drop, reconnect scheduling, liveness failure | | `mutation` | Order placement/cancellation, RFQ operations | Use the `onDiagnostic` callback to observe these: ```ts const client = await createClient({ env: "sandbox", // ... onDiagnostic: (event) => { if (event.traffic === "reconnect") { metrics.increment("ws.reconnect"); } }, }); ``` Each logical WebSocket request or subscription carries a stable `event.correlationId` across its lifecycle, including failures and subscription replay after reconnect. The returned stream exposes the subscription ID as `stream.correlationId`, so an orchestrator can attach application spans, metrics, and logs to the same stream without inspecting wire-level request IDs. REST and OAuth diagnostics expose their response correlation ID at the same top-level field. ## Close behavior ### Closing the client `client.close()` tears down **all** WebSocket sessions — shared, book, and isolated: ```ts client.close(); // All sessions closed, all pending requests rejected, all streams emit "close" ``` This is immediate and suppresses reconnection. Pending requests reject with `"WebSocket session closed"`. ### Closing a stream `stream.close()` sends an `UNSUBSCRIBE` frame and waits for the server's acknowledgement before resolving. The stream's resources are released, but the shared session stays open for other streams: ```ts const trades = client.websocket.public.trades("BTCUSD"); await trades.ready; // wait for subscription ACK // Later... await trades.close(); // sends UNSUBSCRIBE, waits for ACK, then resolves ``` If the unsubscribe times out, the stream is still cleaned up locally. Pass a timeout to control the wait: ```ts await trades.close({ timeoutMs: 5_000 }); ``` ### Closing an order book `book.close()` unsubscribes from the book session and releases the book's resources: ```ts const book = client.websocket.public.orderBook("BTCUSD"); // ... book.close(); // unsubscribes, releases memory ``` ### Listener cleanup with AbortSignal Stream listeners support `AbortSignal` for automatic cleanup without manual `off()` calls: ```ts const controller = new AbortController(); trades.on("message", (trade) => { console.log(trade.p, trade.q); }, { signal: controller.signal }); // Later: remove the listener without a reference to the callback controller.abort(); ``` ## Related - [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — wire-format tables and subscription details - [Order Book deep dive](/tools/typescript-sdk/deep-dives/order-book) — order book synchronization internals - [Data Types deep dive](/tools/typescript-sdk/deep-dives/data-types) — bigint and decimal string handling in WebSocket frames - [Authentication](/tools/typescript-sdk/authentication) — how WebSocket connections authenticate --- URL: https://developer.gemini.com/tools/typescript-sdk/deep-dives/transport-and-signing.md # Deep Dive — Transport and Signing The TypeScript SDK automates request signing, nonce management, retries, and request dispatch. This guide explains transport internals to help you debug authentication and tune client timeouts. ## HMAC signing flow Authenticated REST requests pass through a signing pipeline that constructs three required headers: ### Step by step 1. **Build the payload object**: The SDK combines the API endpoint path with your request parameters and a fresh nonce: ```json { "request": "/v1/order/new", "nonce": 1691234567890, "symbol": "BTCUSD", "amount": "0.01", "price": "50000", "side": "buy", "type": "exchange limit" } ``` 2. **Serialize to JSON**: A `bigint`-safe serializer formats exact numeric values. 3. **Base64-encode**: The JSON string is encoded to base64. 4. **HMAC-SHA384 sign**: The base64 payload is signed with your API secret using HMAC-SHA384. 5. **Attach headers**: Three authentication headers are attached to the HTTP request: | Header | Value | |---|---| | `X-GEMINI-APIKEY` | Your API key | | `X-GEMINI-PAYLOAD` | Base64-encoded JSON payload | | `X-GEMINI-SIGNATURE` | Hex-encoded HMAC-SHA384 signature | The HTTP request body is empty (`Content-Length: 0`). The logical payload is encoded entirely within the `X-GEMINI-PAYLOAD` header. ### Endpoint integrity The `request` field in the payload is always set to the API path. If a caller's parameters contain a `request` key that would override the path, the SDK throws an `EndpointMismatch` error — preventing a signed payload from being routed to the wrong endpoint. ## Nonce serialization and the sign queue The exchange rejects requests with a nonce that is not strictly greater than the previous one (for monotonic mode). When multiple requests are in-flight concurrently, their nonces must arrive **in order** at the server. ### The problem `crypto.subtle.sign()` is asynchronous. If two requests call `nextNonce()` in sequence (getting nonces N and N+1), then sign concurrently, the signing for N+1 could complete before N. The server sees N+1 first, then rejects N as a stale nonce — even though both were valid when issued. ### The solution: sign queue `HmacAuth` chains every `credentialHeaders()` call through a Promise queue (`#signQueue`): ``` Request A: nextNonce() → 100 → sign(100) → resolve headers Request B: nextNonce() → 101 → waits for A's sign → sign(101) → resolve headers Request C: nextNonce() → 102 → waits for B's sign → sign(102) → resolve headers ``` Each signing operation waits for the previous one to complete before starting. This guarantees that credential headers resolve in nonce order, so `fetch()` calls dispatch in nonce order. The queue is fire-and-forget for error propagation: if signing fails for request A, request B still proceeds (the queue's `.then(() => {}, () => {})` swallows A's rejection). Each request independently handles its own signing errors. ### Nonce modes The SDK supports two nonce strategies: | Mode | Behavior | Use case | |---|---|---| | `"monotonic"` (default) | Millisecond timestamp, bumped by 1 if not strictly increasing | General use; safe for high-frequency concurrent requests | | `"time-based"` | Unix timestamp in seconds (`Math.floor(now / 1000)`) | Required by some account-level endpoints | ```ts import { HmacAuth } from "@gemini-markets/sdk/server"; const auth = new HmacAuth({ apiKey: "your-api-key", apiSecret: "your-api-secret", nonceMode: "monotonic", // default }); ``` ## Public vs private dispatch The SDK uses two distinct dispatch paths based on whether an endpoint requires authentication. ### Public endpoints (GET, no auth) Public endpoints like market data use standard HTTP GET with query parameters: ``` GET /v1/pubticker/BTCUSD HTTP/1.1 Host: api.gemini.com ``` No `X-GEMINI-*` headers, no payload signing, no nonce. Parameters are serialized as URL query strings. ### Private endpoints (POST, signed payload) Private endpoints use POST with the signed payload in headers and an empty body: ``` POST /v1/order/new HTTP/1.1 Host: api.gemini.com Content-Length: 0 Content-Type: text/plain Cache-Control: no-cache X-GEMINI-APIKEY: your-api-key X-GEMINI-PAYLOAD: eyJyZXF1ZXN0Ijoi... X-GEMINI-SIGNATURE: a1b2c3d4... ``` The `Content-Type: text/plain` and `Content-Length: 0` are deliberate — the Gemini private REST convention encodes the entire request in headers, not the body. ### Reserved headers The SDK prevents both callers and auth strategies from setting headers that would conflict with the transport envelope: - `X-GEMINI-PAYLOAD`, `Content-Length`, `Content-Type`, `Cache-Control` — always set by the transport - `X-GEMINI-APIKEY`, `X-GEMINI-SIGNATURE` — always set by the auth strategy - `nonce` in request params — reserved for the auth strategy Attempting to set these throws an `SdkError`. ## OAuth dispatch OAuth-authenticated requests use a simpler flow: the `X-GEMINI-PAYLOAD` header is still sent (the exchange always expects it), but there is no HMAC signature and no nonce. ### How it works 1. The `OAuthAuth` strategy loads tokens from the caller-provided `OAuthTokenStore` 2. If the access token is expired (or within the 60-second refresh skew), the token is refreshed automatically 3. The access token is sent as a Bearer token: ``` Authorization: Bearer eyJhbGciOi... ``` ### Serialized refresh Token refresh is serialized through the token store's required `runExclusive` method. This prevents concurrent requests from triggering multiple refresh calls (which would fail because refresh tokens are single-use): ``` Request A: token expired → runExclusive → refresh → save → continue Request B: token expired → runExclusive → load (sees A's fresh token) → continue ``` Without serialization, both A and B would attempt to refresh with the same single-use refresh token. One would succeed; the other would get `invalid_grant` and clear the token store. The lock must be shared by every client instance and process using the store. ### WebSocket auth WebSocket connections authenticate differently from REST. The internal WebSocket session calls `websocketAuthHeaders()` which: 1. Gets a nonce from the auth strategy 2. Base64-encodes just the nonce (not a full payload envelope) 3. Gets credential headers (HMAC signature of the encoded nonce) 4. Sends `X-GEMINI-NONCE` and `X-GEMINI-PAYLOAD` as WebSocket upgrade headers For server-side OAuth-authenticated WebSocket connections, only the `Authorization: Bearer ...` header is sent (no nonce or payload). This does not apply to `BrowserOAuthAuth`: browser OAuth is REST-only, because native browser WebSockets cannot send that upgrade header. ## Retry policy Only operations marked `retryable: true` in their metadata are retried. In practice, this means **all GET-backed reads** (public market data, etc.). POST mutations are **never** retried — replaying a `createNewOrder` could cause a duplicate fill. ### Retried conditions | Condition | Behavior | |---|---| | Network failure (ECONNRESET, ECONNREFUSED, ETIMEDOUT, etc.) | Retry with backoff | | HTTP 429 (rate limited) | Retry, respecting `Retry-After` header if present | | HTTP 502, 503, 504 (server errors) | Retry with backoff | ### Backoff parameters | Parameter | Default | Notes | |---|---|---| | Base delay | **500 ms** | Configurable via `backoff.baseMs` | | Cap | 30 s | Configurable via `backoff.capMs` | | Factor | 2 | Configurable via `backoff.factor` | | Max retries | 5 | Configurable via `maxRetries` | | Jitter | Equal jitter | Half fixed + half random | The backoff formula for attempt N (0-based): `min(capMs, baseMs × factor^N)`, then split into half fixed + half random. This is the same equal-jitter shape used by the WebSocket transport (though with a different base: 500 ms for HTTP vs 250 ms for WebSocket). Note that WebSocket reconnect attempt 0 is **immediate** (delay = 0) to recover from transient drops quickly; HTTP retries always apply the computed backoff. ### Retry-After header When the server sends a `Retry-After` header on a 429 response, the SDK respects it: - **Integer value** — interpreted as seconds (e.g. `Retry-After: 5` → wait 5 seconds) - **HTTP date** — parsed and used as an absolute deadline - **Missing or unparseable** — falls back to the calculated backoff delay ### What is NOT retried - POST mutations (order placement, cancellation, withdrawals, transfers) - Client errors (400, 403, 404, 406) - Auth errors (invalid nonce, invalid signature, missing role) - Validation errors (caught before the request is sent) ## Request validation pipeline For operations with client-side validators (e.g. `createNewOrder`, `placeOrder`, `withdrawCryptoFunds`), validation runs **before** authentication signing. The full pipeline: ``` validate body → add nonce + request fields → JSON.stringify → base64 → HMAC sign → fetch ``` This ordering is important: validation catches malformed requests before any signing work. A validation failure never generates a nonce, so the nonce sequence stays clean. See the [Request Validation deep dive](/tools/typescript-sdk/deep-dives/request-validation) for details on what each operation validates. ## Timeout and cancellation Every request gets a deadline controlled by `timeoutMs` (default: 30 seconds). The SDK uses `AbortSignal` internally to propagate cancellation through every async step. ### How it works ```ts // Default 30-second timeout const ticker = await client.marketData.getTicker({ symbol: "BTCUSD" }); // Custom timeout const ticker = await client.marketData.getTicker({ symbol: "BTCUSD", }, { timeoutMs: 5_000 }); // External cancellation const controller = new AbortController(); const ticker = await client.marketData.getTicker({ symbol: "BTCUSD", }, { signal: controller.signal }); // Cancel from outside controller.abort(); ``` ### Error types | Error | Cause | |---|---| | `RequestTimeoutError` | The deadline elapsed before a response arrived | | `RequestAbortedError` | The caller's `AbortSignal` was aborted | Both propagate through `fetch`, WebSocket operations, and auth signing. An aborted request never leaves a dangling nonce. ### Timeout scope The timeout starts inside the transport's `send()` method — **after** client-side validation has already run. It covers signing → network round-trip → response parsing, but not the validation step. For retried requests, all attempts share a single deadline — later retries have less remaining time. The timeout is per-operation, not per-attempt. ## Related - [Authentication](/tools/typescript-sdk/authentication) — configuring HMAC and OAuth credentials - [Error Handling](/tools/typescript-sdk/errors) — error types and classification - [Request Validation deep dive](/tools/typescript-sdk/deep-dives/request-validation) — client-side validation details - [Data Types deep dive](/tools/typescript-sdk/deep-dives/data-types) — bigint serialization in payloads - [API Specifications](/api-specifications) — full request/response schemas --- URL: https://developer.gemini.com/tools/typescript-sdk/deep-dives/rfq.md # Deep Dive — RFQ Protocol The Request-for-Quote (RFQ) protocol lets makers stream quote requests and submit quotes over WebSocket. The SDK exposes the public RFQ feed, private delivery streams, and maker quote methods. > **Availability:** RFQ streams and methods are available in production for beta testing. Quoting requires an eligible account with quoting capabilities enabled. **Public discovery** (`rfqs()` stream) requires no authentication and runs in browser and server environments. **Quote submission, confirmation, and withdrawal** require authenticated server sessions. ## The flow 1. A taker submits an RFQ, which broadcasts to the public `rfqs` stream. 2. A maker submits a quote in response (`rfq.submitQuote`). 3. The taker accepts the quote; the winning maker receives an event on `rfqDeliveries`. 4. The maker confirms execution (`rfq.confirmQuote`) or withdraws active quotes (`rfq.withdrawQuote`). ## Complete maker example with a pricing hook This server-side example separates venue interactions from quoting logic: - `calculateFmv`: Fair-market value calculation. - `quotePolicy`: Risk, inventory, spread, and collateral filters. Return `null` to pass on an RFQ. - `lastLook`: Verification hook before final fill confirmation. The example uses [`decimal.js`](https://mikemcl.github.io/decimal.js/) for precise financial calculations. Keep prices and quantities as decimal strings; do not convert them to JavaScript `number` values. ```ts import Decimal from "decimal.js"; import { createClient, HmacAuth, WebSocketRequestError, type RfqPrivateDelivery, type RfqPublicEvent, } from "@gemini-markets/sdk/server"; type QuoteDecision = { price: string; quantity: string; validUntil?: number | bigint; clientId?: string; }; type QuoteState = { quoteId: string; decision: QuoteDecision; expiresAt: bigint; }; const client = await createClient({ env: "sandbox", auth: new HmacAuth({ apiKey: process.env.GEMINI_API_KEY!, apiSecret: process.env.GEMINI_API_SECRET!, }), }); // Implement this against your own pricing model or service. Keep the return // value as a decimal string until it enters Decimal. declare const pricingModel: { comboFairValue(legs: RfqPublicEvent["l"]): Promise; }; async function calculateFmv(rfq: RfqPublicEvent): Promise { return new Decimal(await pricingModel.comboFairValue(rfq.l)); } function quantityFor(rfq: RfqPublicEvent, price: Decimal): string { if (rfq.q) return rfq.q; if (!rfq.n) throw new Error(`RFQ ${rfq.r} has neither quantity nor notional`); // The default quantity grid is whole contracts. Use the configured grid if // your venue setup permits fractional RFQ quantities. return new Decimal(rfq.n).div(price).ceil().toFixed(0); } type QuotePolicy = (input: { rfq: RfqPublicEvent; fmv: Decimal; }) => Promise; const quotePolicy: QuotePolicy = async ({ rfq, fmv }) => { // APPLICATION HOOK: replace this with your own inventory/risk/spread logic. // Examples: reject when inventory is too concentrated, widen the spread // during volatile markets, cap quantity by collateral, or return null when // a kill switch is active. This example applies a fixed 1-cent adjustment. const price = fmv.plus("0.01").toDecimalPlaces(4); if (price.lte(0) || price.gte(1)) return null; return { price: price.toFixed(4), quantity: quantityFor(rfq, price), clientId: `rfq-${rfq.r}`, // Omit validUntil to use the service-assigned quoting-window close (`w`). }; }; type LastLook = (input: { delivery: RfqPrivateDelivery; quote: QuoteState; }) => Promise; const lastLook: LastLook = async ({ delivery, quote }) => { // APPLICATION HOOK: re-check inventory, collateral, limits, and the current // model before confirming. Return false to decline the fill. The SDK does // not confirm on your behalf. console.log("last look", delivery.r, quote.decision.price); return true; }; const RFQ_STATE_TTL_MS = 15 * 60 * 1000; const MAX_TRACKED_RFQS = 10_000; const MAX_TRACKED_DELIVERIES = 10_000; const MAX_CONFIRM_RATE_LIMIT_RETRIES = 3; const CONFIRM_RATE_LIMIT_BACKOFF_MS = [100, 250, 500]; const terminalRfqStates = new Set(["FINALIZED", "CANCELLED", "EXPIRED", "FAILED"]); const terminalDeliveryTransitions = new Set(["CONFIRMED", "DECLINED", "FINALIZED", "FAILED"]); const terminalQuoteStatuses = new Set(["WITHDRAWN", "EXPIRED", "WON", "LOST"]); const quotes = new Map(); const consideredRfqs = new Map(); type DeliveryRecord = { state: "in-flight" | "handled" | "failed"; updatedAt: number; }; const deliveryRecords = new Map(); function pruneState(now = Date.now()): void { const nowMs = BigInt(now); for (const [rfqId, quote] of quotes) { if (quote.expiresAt <= nowMs) quotes.delete(rfqId); } for (const [rfqId, expiresAt] of consideredRfqs) { if (expiresAt <= now) consideredRfqs.delete(rfqId); } for (const [deliveryId, record] of deliveryRecords) { if (record.state === "handled" && record.updatedAt + RFQ_STATE_TTL_MS <= now) { deliveryRecords.delete(deliveryId); } } while (consideredRfqs.size > MAX_TRACKED_RFQS) { const oldest = consideredRfqs.keys().next().value; if (oldest === undefined) break; consideredRfqs.delete(oldest); } while (deliveryRecords.size > MAX_TRACKED_DELIVERIES) { const oldest = [...deliveryRecords].find(([, record]) => record.state !== "in-flight")?.[0]; if (oldest === undefined) break; deliveryRecords.delete(oldest); } } // Call this only after querying the RFQ/order state for a failed mutation. // Keep the delivery deduplicated if the mutation was applied; otherwise allow // a redelivery to retry it safely. function reconcileFailedDelivery(deliveryId: string, mutationApplied: boolean): void { const record = deliveryRecords.get(deliveryId); if (record?.state !== "failed") return; if (mutationApplied) { deliveryRecords.set(deliveryId, { state: "handled", updatedAt: Date.now() }); } else { deliveryRecords.delete(deliveryId); } } async function quoteRfq(rfq: RfqPublicEvent): Promise { pruneState(); if (rfq.w !== undefined && BigInt(rfq.w) <= BigInt(Date.now())) return; const fmv = await calculateFmv(rfq); const decision = await quotePolicy({ rfq, fmv }); if (!decision) return; // Pricing and risk checks can take time; never submit after the window closes. if (rfq.w !== undefined && BigInt(rfq.w) <= BigInt(Date.now())) return; const response = await client.websocket.private.rfq.submitQuote({ rfqId: rfq.r, price: decision.price, quantity: decision.quantity, ...(decision.validUntil === undefined ? {} : { validUntil: decision.validUntil }), ...(decision.clientId === undefined ? {} : { clientId: decision.clientId }), }); if (!response.result) throw new Error(`submitQuote returned no result for ${rfq.r}`); quotes.set(rfq.r, { quoteId: response.result.quoteId, decision, expiresAt: BigInt(Date.now()) + BigInt(RFQ_STATE_TTL_MS), }); } class UnknownMutationOutcome extends Error { constructor(cause: unknown) { super("RFQ confirmation outcome is unknown; reconcile before retrying", { cause }); this.name = "UnknownMutationOutcome"; } } function sleep(milliseconds: number): Promise { return new Promise((resolve) => setTimeout(resolve, milliseconds)); } async function confirmQuoteWithRetry(input: { rfqId: string; quoteId: string; confirm: boolean; }): Promise { for (let attempt = 0; ; attempt += 1) { try { await client.websocket.private.rfq.confirmQuote(input); return; } catch (error) { if (!(error instanceof WebSocketRequestError)) throw error; // A rate-limit response means the request was rejected before the // mutation ran, so a short bounded retry is safe. Other 4xx responses // are definitive business rejections; 5xx responses have an unknown // mutation outcome and must be reconciled instead of retried. if (error.status >= 400 && error.status < 500 && error.status !== 429) { throw error; } if (error.status !== 429) throw new UnknownMutationOutcome(error); if (attempt >= MAX_CONFIRM_RATE_LIMIT_RETRIES) throw new UnknownMutationOutcome(error); await sleep(CONFIRM_RATE_LIMIT_BACKOFF_MS[attempt] ?? 500); } } } async function handleAccepted(delivery: RfqPrivateDelivery): Promise { if ( terminalDeliveryTransitions.has(delivery.x) || terminalRfqStates.has(delivery.S) || (delivery.qs !== undefined && terminalQuoteStatuses.has(delivery.qs)) ) { quotes.delete(delivery.r); return; } if (delivery.x !== "ACCEPTED" || !delivery.q) return; const quote = quotes.get(delivery.r); if (!quote || quote.quoteId !== delivery.q) return; const confirm = await lastLook({ delivery, quote }); try { await confirmQuoteWithRetry({ rfqId: delivery.r, quoteId: delivery.q, confirm, }); } catch (error) { if (error instanceof WebSocketRequestError) { // A terminal business rejection is definitive. Do not redeliver this // quote, but preserve 5xx/unknown outcomes for reconciliation below. quotes.delete(delivery.r); console.error("RFQ confirmation was rejected", delivery.r, error); return; } throw new UnknownMutationOutcome(error); } quotes.delete(delivery.r); } async function processDelivery(delivery: RfqPrivateDelivery): Promise { pruneState(); const existing = deliveryRecords.get(delivery.i); if (existing?.state === "handled" || existing?.state === "in-flight" || existing?.state === "failed") return; if (deliveryRecords.size >= MAX_TRACKED_DELIVERIES) { console.error("RFQ delivery deduplication cache is full; reconcile before processing", delivery.i); return; } deliveryRecords.set(delivery.i, { state: "in-flight", updatedAt: Date.now() }); try { await handleAccepted(delivery); deliveryRecords.set(delivery.i, { state: "handled", updatedAt: Date.now() }); } catch (error) { if (error instanceof UnknownMutationOutcome) { // Keep the delivery suppressed until an operator reconciles the // mutation outcome; an automatic retry could duplicate a confirmation. deliveryRecords.set(delivery.i, { state: "failed", updatedAt: Date.now() }); console.error("RFQ confirmation needs reconciliation before retrying", delivery.i, error); } else { // lastLook failed before the mutation was sent, so a redelivery may // safely retry it. Do not finalize deduplication in that case. deliveryRecords.delete(delivery.i); console.error("RFQ delivery handling failed before mutation; retrying on redelivery", delivery.i, error); } } pruneState(); } const rfqs = client.websocket.public.rfqs(); const deliveries = client.websocket.private.rfqDeliveries({ scope: "account" }); rfqs.on("message", (rfq) => { pruneState(); if (terminalRfqStates.has(rfq.S)) { quotes.delete(rfq.r); consideredRfqs.set(rfq.r, Date.now() + RFQ_STATE_TTL_MS); return; } if (rfq.S !== "OPEN" || consideredRfqs.has(rfq.r)) return; consideredRfqs.set(rfq.r, Date.now() + RFQ_STATE_TTL_MS); // Stream listeners should stay short; mutations are intentionally not // retried because replaying a quote could create an unexpected position. void quoteRfq(rfq).catch((error) => { console.error("RFQ quote failed", rfq.r, error); }); }); deliveries.on("message", (delivery) => { // Authenticated lifecycle delivery is at-least-once. A delivery is marked // handled only after its work succeeds; unknown mutation outcomes stay in // a failed state until reconciled instead of being retried blindly. void processDelivery(delivery); }); await Promise.all([rfqs.ready, deliveries.ready]); ``` This process is now listening for new auctions and maker acceptances. Close the client during application shutdown with `client.close()`. Because RFQ mutations are one-shot and the quote is immutable, treat an unknown submission or confirmation result as an operational event to reconcile rather than blindly retrying it. ## Watching for requests The public `rfqs` stream emits [`RfqPublicEvent`](/tools/typescript-sdk/reference/websocket) messages: ```ts const rfqs = client.websocket.public.rfqs(); rfqs.on("message", (event) => { // event.r — RFQ ID // event.l — legs (RfqLeg[]): each has c (contract), o (side), and s (leg's own symbol, when available) // event.S — lifecycle state // event.q — quantity (optional) // event.s — symbol (optional) console.log("RFQ", event.r, "state", event.S); }); await rfqs.ready; ``` ## Submitting a quote ```ts const quote = await client.websocket.private.rfq.submitQuote({ rfqId: event.r, price: "0.65", // decimal string quantity: "100", // decimal string validUntil: 1710547200000n, // optional, millisecond timestamp (bigint) clientId: "maker-fill-123", // optional, returned as `c` on the maker fill }); // quote: RfqSubmitQuoteResponse ``` `RfqSubmitQuoteParams`: | Field | Type | Required | Meaning | | --- | --- | --- | --- | | `rfqId` | `string` | Yes | The RFQ being quoted | | `price` | `string` | Yes | Quote price (decimal string) | | `quantity` | `string` | Yes | Quote quantity (decimal string) | | `validUntil` | `number \| bigint` | No | Expiry (millisecond timestamp) | | `clientId` | `string` | No | Client order ID for tracking the maker fill; returned as `c` in the authenticated `orderUpdate` event. Printable ASCII, maximum 36 characters. Keep it unique per maker account. | ## Confirming a quote ```ts await client.websocket.private.rfq.confirmQuote({ rfqId: event.r, quoteId: quote.quoteId, confirm: true, }); ``` `RfqConfirmQuoteParams`: | Field | Type | Required | Meaning | | --- | --- | --- | --- | | `rfqId` | `string` | Yes | The RFQ | | `quoteId` | `string` | Yes | The quote being confirmed | | `confirm` | `boolean` | Yes | `true` to confirm, `false` to reject | ## Withdrawing a quote ```ts await client.websocket.private.rfq.withdrawQuote({ rfqId: event.r, quoteId: quote.quoteId, }); ``` `RfqWithdrawQuoteParams`: | Field | Type | Required | Meaning | | --- | --- | --- | --- | | `rfqId` | `string` | Yes | The RFQ | | `quoteId` | `string` | Yes | The quote to withdraw | ## Private delivery stream The authenticated `rfqDeliveries` stream emits [`RfqPrivateDelivery`](/tools/typescript-sdk/reference/websocket) messages — the private outcome of your quotes: ```ts const deliveries = client.websocket.private.rfqDeliveries({ scope: "account" }); deliveries.on("message", (delivery) => { // delivery.i — delivery ID // delivery.r — RFQ ID // delivery.S — lifecycle state // delivery.qs — quote status (optional) // delivery.p — price (optional) // delivery.sz — size (optional) console.log("Delivery", delivery.i, "state", delivery.S); }); ``` `rfqDeliveries` takes a `scope` (`"account"` or `"session"`), like the `orders` stream. ## Combos RFQ often targets multi-leg combos. Create a combo instrument via REST before quoting: ```ts const combo = await client.predictions.createCombo({ legs: [ { contractId: "101", requiredOutcome: "Yes" }, { contractId: "202", requiredOutcome: "No" }, ], }); if (!combo.combo.instrumentSymbol) { throw new Error("The combo has no instrument symbol"); } const lookup = await client.predictions.getComboByInstrumentSymbol({ instrumentSymbol: combo.combo.instrumentSymbol, }); ``` See the [Combos Reference](/tools/typescript-sdk/reference/predictions/combos) for combo operations. ## What's next - [WebSocket Reference](/tools/typescript-sdk/reference/websocket#rfq-quote-methods) — RFQ type signatures - [Maker Integration](/prediction-markets/combos-rfq/maker-integration) — lifecycle, timing, and partial-fill risks - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and bigint timestamps --- URL: https://developer.gemini.com/tools/typescript-sdk/deep-dives/request-validation.md # Deep Dive — Request Validation For state-changing operations, the SDK validates request bodies against the documented shape **before** signing or sending them. A malformed request fails fast with a typed `ValidationError` and never hits the network. ## Why validate client-side Authenticated mutations are signed with your nonce and secret. Sending a request the server will reject wastes a nonce and a round trip, and the resulting server error is often less specific than a local check. Validating first means: - Bad requests fail before authentication — no wasted nonce. - The error names the exact field and rule that failed. - You catch mistakes in development, not from a production 400. ## ValidationError ```ts import { ValidationError } from "@gemini-markets/sdk/server"; try { await client.trading.createNewOrder({ symbol: "BTCUSD", amount: "not-a-number", // invalid decimal price: "50000", side: "buy", type: "exchange limit", }); } catch (err) { if (err instanceof ValidationError) { console.log(err.operation); // "trading.createNewOrder" console.log(err.field); // "amount" console.log(err.rule); // "format" console.log(err.message); // "amount must be a quoted decimal string" } } ``` `ValidationError` extends `SdkError` and carries three fields for programmatic handling: | Field | Meaning | | --- | --- | | `operation` | The operation that failed (e.g. `"trading.createNewOrder"`) | | `field` | The offending field name | | `rule` | The rule that failed (`"required"`, `"type"`, etc.) | ## Validated operations Validation covers state-changing operations across trading, prediction markets, clearing, and account services: **Trading** — `createNewOrder`, `cancelOrder`, `cancelAllActiveOrders`, `cancelAllSessionOrders`, `getOrderStatus`, `wrapOrder` **Prediction Markets** — `placeOrder`, `placeOrderBatch`, `cancelOrder`, `cancelOrderBatch`, `createCombo` **Clearing & Instant** — `createNewClearingOrder`, `createNewBrokerOrder`, `confirmClearingOrder`, `cancelClearingOrder`, `executeInstantOrder` **Account Services** — `withdrawCryptoFunds`, `transferBetweenAccounts`, `stakeCryptoFunds`, `unstakeCryptoFunds`, `createNewApprovedAddress`, `removeApprovedAddress`, `createNewDepositAddress`, `createNewAccount`, `renameAccount`, `addBank`, `addBankCAD`, `revokeOAuthToken` Validation is operation-specific. Most read-only operations pass inputs directly to the server, but some (like `trading.getOrderStatus`) validate that required identifiers are present and well-formed before sending. ## What gets checked Validators enforce the documented shape of each body: required fields are present, and fields have the correct type and format. Common formats: | Rule | Checks | | --- | --- | | Decimal | Numeric string like `"50000.25"` (prices, quantities, amounts) | | Integer ID | Digit string like `"12345"` | | UUID | RFC 4122 UUID (`clientTransferId` on transfers and withdrawals) | | Boolean | Actual `boolean`, not `"true"` | | Required | Field is present and non-null | Validation runs on the caller-supplied body before the SDK adds transport fields (`nonce`, `request`) and signs. The check is purely structural — it does not contact the server, so it cannot catch business-rule failures (insufficient funds, unknown symbol); those still surface as [`ApiError`](/tools/typescript-sdk/errors) subclasses from the server. ## Ordering with terms acceptance For prediction market order placement, validation runs first, then the SDK sends the request. Terms acceptance is a server business rule and is returned by the endpoint when required. The sequence: 1. `ValidationError` if the body shape is wrong. 2. The signed request is sent. 3. `AcceptTermsRequired` if terms are not accepted ([see Patterns](/tools/typescript-sdk/patterns#prediction-markets-terms-acceptance)). ## What's next - [Error Handling](/tools/typescript-sdk/errors) — the full error hierarchy - [REST Reference](/tools/typescript-sdk/reference/overview) — every operation and its access level --- URL: https://developer.gemini.com/tools/typescript-sdk/deep-dives/order-book.md # Deep Dive — Order Book Reconstruction `client.orderBook(symbol)` returns a `LiveOrderBook` — a local L2 order book the SDK keeps synchronized from the WebSocket depth stream. This page explains how it stays correct, how it detects and recovers from gaps, and the guarantees it makes. ## The snapshot + diff model The exchange does not stream the full book on every change. Instead: 1. The SDK subscribes to the depth stream and requests an initial **snapshot** — the complete book at a point in time. 2. Every subsequent message is a **diff** ([`DepthUpdate`](/tools/typescript-sdk/reference/websocket#depthupdate)) — only the price levels that changed. 3. The SDK applies each diff in sequence to keep its local copy current. The first `"update"` event you receive carries the **full book** (the snapshot). Every event after that carries only the changed levels. ```ts const book = client.orderBook("BTCUSD"); book.on("update", (lob, delta) => { // `delta` = only the levels that changed on this update // `lob` = the full current book, queried on demand console.log(lob.bestBid(), lob.bestAsk()); }); ``` ## Sequence integrity Each diff carries two sequence IDs: `U` (first update ID) and `u` (last update ID). The SDK tracks the last applied `u` and enforces strict ordering: - **Stale diff** (`u <= lastUpdateId`): already covered, silently dropped. - **Contiguous diff** (`U <= lastUpdateId < u`): applied, `lastUpdateId` advances to `u`. - **Gap** (`U > lastUpdateId`): a frame was missed. The book can no longer be trusted — it raises `ResyncRequiredError` internally and goes stale. Gemini's depth stream overlaps at `U == lastUpdateId` (unlike a strictly contiguous stream), so any `U` strictly greater than the last applied ID indicates a real gap, not normal overlap. ## Self-healing When a gap is detected — or a malformed frame arrives, or the connection reconnects — the book: 1. Marks itself **stale** (`live = false`). 2. Emits a single `"resync"` event (deduplicated: one per stale period). 3. Requests a fresh snapshot in the background. 4. On snapshot arrival, rebuilds and emits a full-book `"update"`. You never call anything to trigger recovery — it is automatic. Your job is to respect the `"resync"` signal: ```ts book.on("resync", () => { // The book is stale and rebuilding. Do not trade on it until // the next "update" arrives with the fresh snapshot. console.warn("Order book resyncing — treat current state as unavailable"); }); ``` ## Stale reads return nothing While stale, every read method returns empty — a gapped book must never look tradeable: | Method | While live | While stale | | --- | --- | --- | | `bestBid()` / `bestAsk()` | `Level` | `undefined` | | `topN(side, n)` | `Level[]` | `[]` | | `spread()` / `mid()` | `number` | `undefined` | | `snapshot()` | `{ bids, asks }` | `{ bids: [], asks: [] }` | This means you cannot accidentally read a torn book: if `bestBid()` returns `undefined`, the book is either not yet initialized or currently resyncing. ## Reads ```ts book.bestBid(); // { price, qty } | undefined — highest bid book.bestAsk(); // { price, qty } | undefined — lowest ask book.topN("bids", 10); // Level[] — top 10 bids, best-first book.spread(); // number | undefined — ask − bid (display only) book.mid(); // number | undefined — midpoint (display only) book.snapshot(); // { bids: Level[], asks: Level[] } — full book copy ``` `spread()` and `mid()` return floating-point numbers for display. Do not use them for exact execution math — prices on the wire are decimal strings (see [Data Types](/tools/typescript-sdk/deep-dives/data-types)) and converting to float loses precision. ## Price level identity Levels are keyed by a **canonical price string**, so `"0.50"` and `"0.5"` map to the same level. This guarantees a removal (`quantity: "0"`) can never leave a stale duplicate at a differently-formatted price. ## Separate session The order book runs on its own WebSocket session, isolated from public streams (`trades`, `bookTicker`, etc.). A reconnect or failure on one does not stall the other. In sandbox, the SDK uses a dedicated snapshot stream automatically. ## Events ```ts book.on("update", (lob, delta) => { /* book changed */ }); book.on("resync", () => { /* stale, rebuilding — protect yourself */ }); book.on("error", (err) => { /* SdkError — malformed frame, etc. */ }); // Auto-remove on abort: const controller = new AbortController(); book.on("update", handler, { signal: controller.signal }); controller.abort(); book.close(); // stop updates, remove listeners, release the stream ``` Errors are always delivered as `SdkError`. A malformed depth frame (e.g. a level that isn't a `[price, quantity]` string tuple) is wrapped so the `"error"` listener always receives an `SdkError`, and the book goes stale before the listener runs — a throwing listener cannot bypass recovery. ## What's next - [WebSocket Reference](/tools/typescript-sdk/reference/websocket#depthupdate) — the `DepthUpdate` wire format - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and precision --- URL: https://developer.gemini.com/tools/typescript-sdk/deep-dives/data-types.md # Deep Dive — Data Types The SDK preserves exchange values exactly as sent, without silent precision loss. This means some fields are `bigint` and monetary values are strings. Understanding why prevents subtle bugs. ## Numeric types in REST responses The SDK's generated types reflect what the OpenAPI spec declares for each field. **Not all monetary values are the same type:** - **String fields** — order prices, quantities, and amounts on prediction-market and trading endpoints are strings (e.g. `"50123.45"`). Use a decimal library for arithmetic. - **Number fields** — some endpoints return `number` for amounts and rates. Examples: `Balance.amount`, `Balance.available`, `FxRate.rate`, `SymbolDetails.tick_size`, `SymbolDetails.quote_increment`, and candle OHLCV values. Check the TypeScript types in your IDE to see which fields are `string` vs `number` for each endpoint. The general rule: **WebSocket prices are always strings; REST responses vary by endpoint.** ```ts // REST: Balance amounts are number const balances = await client.account.getAvailableBalances({ account: "primary" }); // balances[0].amount is number, not string // REST: Order prices are string const order = await client.predictions.placeOrder({ price: "0.65", /* ... */ }); // WebSocket: always strings trades.on("message", (t) => { // t.p is a string — "50123.45" }); ``` For financial arithmetic on string fields, use a decimal library: ```ts import Decimal from "decimal.js"; const notional = new Decimal(order.price).times(order.quantity); ``` The order book's `spread()` and `mid()` return floats for display only — do not use them for exact execution math. ## BigInt for large integers The exchange sends integers that exceed JavaScript's safe integer range (`Number.MAX_SAFE_INTEGER`, 2^53 − 1) — specifically nanosecond timestamps and sequence IDs. The SDK preserves these as `bigint`: ```ts const trades = client.websocket.public.trades("BTCUSD"); trades.on("message", (t) => { // t.E and t.t are number | bigint; unsafe values are bigint. const millis = Number(BigInt(t.E) / 1_000_000n); // convert ns → ms for Date console.log(new Date(millis)); }); ``` Fields typed `number | bigint` are `bigint` when the value exceeds the safe range and `number` otherwise. To be safe, always handle both — `BigInt(x)` normalizes either: ```ts const eventTimeNs = BigInt(t.E); ``` ### Why this matters A plain `JSON.parse` silently rounds integers beyond 2^53 to the nearest double, corrupting IDs and timestamps with no error. A trade ID like `9007199254740993` would round to `9007199254740992` — a different, wrong ID. The SDK uses a runtime-independent parser that recovers the exact digits and preserves unsafe integer literals as `bigint`. Strings (prices), floats, and safe integers are unchanged. Lossless parsing is available across the supported browser, server, and worker runtimes. See [runtime compatibility](/tools/typescript-sdk/patterns#runtime-compatibility). ## int64 request inputs Where an endpoint accepts a 64-bit integer input, the SDK's generated types accept `bigint | number` so you can pass either: ```ts await client.marketData.getFXRate({ symbol: "EURUSD", timestamp: 1710547200000n, // milliseconds since epoch }); ``` Response `int64` paths are normalized automatically based on generated operation metadata — you always receive `bigint` for those fields. ## Wire format field names WebSocket payloads use compact single-letter field names (`p`, `q`, `E`, `i`, `X`, …). This is the exchange's wire format, preserved by the SDK rather than renamed. The [WebSocket Reference](/tools/typescript-sdk/reference/websocket#wire-format) maps every letter to its meaning. REST responses use full field names. ## What's next - [Order Book Reconstruction](/tools/typescript-sdk/deep-dives/order-book) — where sequence IDs and precision matter - [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — full field tables --- URL: https://developer.gemini.com/rest-api/prediction-markets/volume/get-volume.md # Get Prediction Market Volume ## Requests Select one completed UTC date from the rolling one-year window. The current UTC day is unavailable. The daily endpoint returns flat category-path rows for the whole day: ```bash curl "https://api.gemini.com/v1/prediction-markets/volume/2026-07-20" ``` Append `/hourly` to return one flat category row for each completed UTC hour. Each hourly row includes its `periodStart`: ```bash curl "https://api.gemini.com/v1/prediction-markets/volume/2026-07-20/hourly" ``` Dates before the `2025-12-15` launch and post-launch dates with any missing source hour return `404 NOT_FOUND`; the endpoint never returns a partial day. , }, { heading: "Responses", children: , }, ]} /> ## Hourly volume , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/terms/get-terms.md # Get Terms , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/terms/get-terms-status.md # Get Terms Status " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader or Auditor role assigned. See Roles for more information.

    The OAuth scope must have orders:read assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Authenticated Payload", children: (

    Sign the private REST payload with request: "/v1/prediction-markets/terms/status" and a fresh nonce. This endpoint has no endpoint-specific request body.

    ), }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/terms/accept-terms.md # Accept Terms " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Authenticated Payload", children: (

    Sign the private REST payload with request: "/v1/prediction-markets/terms/accept" and a fresh nonce. This endpoint accepts the latest configured terms version and has no endpoint-specific request body.

    ), }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/rewards/list-maker-rebate-payouts.md # List Maker Rebate Payouts " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/rewards/list-liquidity-rewards-events.md # List Liquidity Rewards Events , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/rewards/get-maker-rebate-rates.md # Get Maker Rebate Rates , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/rewards/get-maker-rebate-lifetime-summary.md # Get Maker Rebate Lifetime Summary " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/rewards/get-liquidity-rewards-lifetime-summary.md # Get Liquidity Rewards Lifetime Summary " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/rewards/get-liquidity-rewards-daily-summary.md # Get Liquidity Rewards Daily Summary " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/rewards/get-liquidity-rewards-config.md # Get Liquidity Rewards Config , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/positions/get-volume-metrics.md # Get Volume Metrics " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { eventTicker: "FED260318", startTime: 1772412364000, endTime: 1772671564000, }, }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/positions/get-settled-positions.md # Get Settled Positions " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> ## Key Features ### Settlement Data Each settled position includes: - **Payout**: Total amount received from settlement in USD - **Cost Basis**: Total amount spent to acquire the position (nullable) - **Realized P&L**: Realized profit/loss from prior trading activity (nullable) - **Net Profit**: Net profit or loss calculated as `payout - costBasis + realizedPnl` (nullable) - **Resolution Side**: How the contract resolved (`YES`, `NO`, or `UNSPECIFIED`) - **Outcome**: The side held (`YES` or `NO`) ### Position Values - **position**: Raw signed position value (positive for YES, negative for NO) - **positionQuantity**: Absolute quantity of contracts held ### Cash-Out Data (Optional) When `withCashOuts=true`, the response includes additional fields for positions that were exited early before settlement: - **cashOuts**: Array of early position exits with proceeds, cost basis, and net profit - **totalCashOutProceeds**: Sum of all proceeds from cash-outs - **totalCashOutCostBasis**: Sum of all cost basis for cash-outs - **totalCashOutNetProfit**: Sum of all net profits from cash-outs ### Query Parameters - **eventTicker**: Filter by specific event (e.g., "BTC100K2025") - **limit**: Maximum results to return (1-1000, default: 1000) - **offset**: Pagination offset (default: 0) - **sort**: Sort order (`date`, `-date`, `payout`, `+payout`, `-payout`) - **search**: Case-insensitive substring filter (min 3, max 64 characters) - **category**: Filter by category or descendants (e.g., "sports") - **withCashOuts**: Include cash-out data (default: false) ## Example Response ```json { "positions": [ { "instrumentId": 16789219, "instrumentSymbol": "GEMI-BTC100K2025-HI100000", "position": "100", "positionQuantity": "100", "outcome": "yes", "payout": "100.00", "resolutionSide": "yes", "settledAt": "2026-07-15T12:00:00.000Z", "costBasis": "65.00", "realizedPnl": "5.00", "netProfit": "40.00", "contractMetadata": { "eventTicker": "BTC100K2025", "eventName": "Will Bitcoin reach $100,000 in 2025?", "contractName": "BTC100K2025-HI100000" } } ], "total": 1, "totalPayout": "100.00", "totalCostBasis": "65.00", "totalNetProfit": "40.00" } ``` ## Example with Cash-Outs ```bash curl -X POST "https://api.gemini.com/v1/prediction-markets/positions/settled?withCashOuts=true&limit=10" \ -H "X-GEMINI-APIKEY: mykey" \ -H "X-GEMINI-PAYLOAD: payload" \ -H "X-GEMINI-SIGNATURE: signature" ``` Response includes additional `cashOuts`, `totalCashOutProceeds`, `totalCashOutCostBasis`, and `totalCashOutNetProfit` fields. --- URL: https://developer.gemini.com/rest-api/prediction-markets/positions/get-positions.md # Get Positions " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See{" "} Roles {" "} for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/order-management/place-order.md # Place Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { symbol: "GEMI-FEDJAN26-DN25", orderType: "limit", side: "buy", quantity: "100", price: "0.65", outcome: "yes", timeInForce: "good-til-cancel", }, }, }} sections={[ { heading: "Terms", children: (

    Before sending a Prediction Markets order, check Get Terms Status. If the latest terms are not accepted, display Get Terms and call Accept Terms, then retry the order.

    ), }, { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "REST vs WebSocket Payloads", children: ( <>

    WebSocket is the preferred path for active trading and market making. REST order placement is for reference and one-off server workflows. REST and WebSocket order payloads are not interchangeable:

    Task WebSocket order.place REST POST /v1/prediction-markets/order
    Outcome eventOutcome: "YES" outcome: "yes"
    Order type type: "LIMIT" orderType: "limit"
    Maker-only timeInForce: "MOC" timeInForce: "good-til-cancel", makerOrCancel: true
    Side side: "BUY" side: "buy"
    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/order-management/place-batch-orders.md # Place Batch Orders " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { orders: [ { symbol: "GEMI-FEDJAN26-DN25", orderType: "limit", side: "buy", quantity: "100", price: "0.65", outcome: "yes", timeInForce: "good-til-cancel", }, { symbol: "GEMI-FEDJAN26-DN25", orderType: "limit", side: "sell", quantity: "50", price: "0.35", outcome: "no", timeInForce: "good-til-cancel", }, ], }, }, }} sections={[ { heading: "Terms", children: (

    Before sending a Prediction Markets order, check Get Terms Status. If the latest terms are not accepted, display Get Terms and call Accept Terms, then retry the batch.

    ), }, { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Batch Behavior", children: (
    • The complete {`{"orders":[...]}`} payload is encoded and signed once.
    • Gemini validates the batch size and every order before submitting anything. Any up-front validation, account, terms, or risk failure rejects the entire request.
    • After validation, orders are submitted sequentially and results remain aligned with request order.
    • An exchange rejection such as InsufficientFunds is returned in that entry's result. Other valid entries continue to be submitted.
    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/order-management/get-order-history.md # Get Order History " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { status: "filled", from: 1775001600000, to: 1775088000000, limit: 1000, }, }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/order-management/get-active-orders.md # Get Active Orders " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/order-management/cancel-order.md # Cancel Order " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { orderId: 12345678, }, }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/order-management/cancel-batch-orders.md # Cancel Batch Orders " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { orderIds: [12345678, "12345679"], }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must have the Trader role assigned. See Roles for more information.

    The OAuth scope must have orders:create assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Batch Behavior", children: (
    • The complete {`{"orderIds":[...]}`} payload is encoded and signed once.
    • Each order ID may be a JSON integer or a quoted numeric string.
    • Gemini validates every ID before attempting any cancellation. An invalid ID, an empty batch, or more than 20 entries rejects the entire request.
    • After validation, cancellations are attempted sequentially and results remain aligned with request order.
    • An exchange rejection such as OrderNotFound is returned in that entry's result. Other valid cancellations continue to be attempted.
    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/events/list-upcoming-events.md # List Upcoming Events , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/events/list-recently-settled-events.md # List Recently Settled Events , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/events/list-newly-listed-events.md # List Newly Listed Events , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/events/list-events.md # List Events , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/events/list-event-categories.md # List Event Categories , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/events/get-event.md # Get Event , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/events/get-event-strike.md # Get Strike Price for Event , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/combos/list-combos.md # List Combos This Combo Prediction Markets endpoint is not currently enabled in production.

    }} example={{ request: { method: "GET", url: "https://api.gemini.com/v1/prediction-markets/combos", headers: [], }, }} sections={[ { heading: "Query Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/combos/get-combo.md # Get Combo This Combo Prediction Markets endpoint is not currently enabled in production.

    }} example={{ request: { method: "GET", url: "https://api.gemini.com/v1/prediction-markets/combos/GEMI-CMB-0526-A7F3B2C1D4E5", headers: [], }, }} sections={[ { heading: "Path Parameters", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/prediction-markets/combos/create-combo.md # Create Combo This Combo Prediction Markets endpoint is not currently enabled in production.

    }} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/prediction-markets/combos", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { legs: [ { contractId: "101", requiredOutcome: "Yes" }, { contractId: "202", requiredOutcome: "No" }, ], }, }, }} sections={[ { heading: "Access", children: (

    This endpoint requires signed private REST authentication, the PredictionsNewOrder permission, and an unrestricted trading account. The account is derived from the authenticated API key; do not include an account ID in the request.

    ), }, { heading: "Canonicalization", children: (

    The complete set of legs defines a combo. Reordering the same legs does not create another combo: a new canonical combo returns 201 with alreadyExisted: false, while an existing one returns 200 with alreadyExisted: true.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/oauth/revoke-access-token.md # Revoke Access Token " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/oauth/revokeByToken", }, }, }} sections={[ { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/oauth/refresh-access-token.md # Refresh Access Token Public clients omit client_secret on refresh. Refresh requests never include code_verifier., }} example={{ request: { method: "POST", url: "https://exchange.gemini.com/auth/token", headers: [], body: { client_id: "my_id", client_secret: "my_secret", refresh_token: "215c5a89-6df7-457b-ba0b-70695da8c91f", grant_type: "refresh_token", }, }, }} sections={[ { heading: "Request Body", children: ( Secret of your application, provided when you register a confidential client in API settings. Confidential clients only — public clients must not send this., }, { name: "refresh_token", type: "string", required: true, description: "Your refresh token.", }, { name: "grant_type", type: "string", required: true, description: 'The literal string "refresh_token".', example: "refresh_token", }, ]} /> ), }, { heading: "Responses", children: ( ), }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/oauth/authorization-token-request.md # Get Access Token Public clients send code_verifier instead of client_secret — omit client_secret entirely, or the request will fail. See Public Clients and PKCE., }} example={{ request: { method: "POST", url: "https://exchange.gemini.com/auth/token", headers: [], body: { client_id: "my_id", client_secret: "my_secret", code: "90123465-86ee-44ef-b4e3-835cc89bc8a3", redirect_uri: "www.example.com/redirect", grant_type: "authorization_code", }, }, }} sections={[ { heading: "Request Body", children: ( Unique ID of your application. This is provided in your API settings., }, { name: "client_secret", type: "string", required: false, description: <>Secret of your application, provided when you register a confidential client in API settings. Confidential clients only — public clients must not send this, and a request that includes it will fail., }, { name: "code", type: "string", required: true, description: "The authorization code received from the Authorization Request.", }, { name: "redirect_uri", type: "string", required: true, description: "Must match the redirect_uri provided in the Authorization Request.", }, { name: "grant_type", type: "string", required: true, description: 'The literal string "authorization_code".', example: "authorization_code", }, { name: "code_verifier", type: "string", required: false, description: <>Required for public clients. The original code_verifier you generated before the authorization request (43–128 characters from [A-Za-z0-9-._~]). Gemini hashes it and compares it to the code_challenge you sent., }, ]} /> ), }, { heading: "Responses", children: ( ), }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/oauth/authorization-request.md # Authorization Request Public clients (no client_secret) must use PKCE: send code_challenge, code_challenge_method=S256, and a non-empty state. See Public Clients and PKCE., }} example={{ request: { method: "GET", url: "https://exchange.gemini.com/auth?client_id=my_id&response_type=code&redirect_uri=www.example.com/redirect&state=82350325&scope=balances:read,orders:create", headers: [], }, }} sections={[ { heading: "URL Parameters", children: ( Unique ID of your application from your API settings., }, { name: "response_type", type: "string", required: true, description: 'The literal string "code".', example: "code", }, { name: "redirect_uri", type: "string", required: true, description: "The callback URL where Gemini redirects users after authorization. Must match an approved redirect URI configured in your API settings.", }, { name: "state", type: "string", required: true, description: "An opaque string used to protect against CSRF attacks. Gemini returns this value unchanged in the callback redirect. Verify that the returned state matches the original request before exchanging the code.", }, { name: "scope", type: "string", required: true, description: <>A comma-separated list of OAuth scopes corresponding to the access you are requesting. These scopes must be included in your list of scopes in your app registration., }, { name: "code_challenge", type: "string", required: false, description: <>Required for public clients. The PKCE code challenge: BASE64URL-no-padding(SHA-256(code_verifier)). Always 43 characters for the S256 method. See Public Clients and PKCE., }, { name: "code_challenge_method", type: "string", required: false, description: <>Required for public clients. The literal string S256. plain is not accepted., example: "S256", }, ]} /> ), }, { heading: "Responses", children: ( ), }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/admin/subaccounts.md # Subaccounts Subaccounts isolate balances, orders, rate limits, and API keys under a single account group. Use subaccounts to separate trading strategies, contain risk, or sandbox autonomous agents. A Master API key manages all accounts in a group, and internal transfers move funds between them instantly. **Workflow**: Issue a Master API key, create accounts via API, target specific accounts by including their `account` slug (e.g. `strategy-trend`) in request payloads, and rebalance capital using internal transfers. ## The model Your relationship with Gemini is organized into three layers: | Layer | What it is | Created by | |-------|------------|-----------| | **Account group** | Top-level container provisioned during onboarding. Holds KYC records, approved withdrawal addresses, and group-wide settings. | Gemini onboarding | | **Account** | Member of a group. Holds balances, orders, trades, and transfers. Has a type (`exchange` or `custody`). Managed via Master keys or dedicated account keys. | `POST /v1/account/create` or Gemini support | | **User** | Individual user with KYC verification. Users can belong to multiple accounts and groups. Roles are assigned per account. | Gemini onboarding | ``` ┌──────────────────────────────────────────────┐ │ Account Group │ │ KYC · approved addresses · group settings │ └─────────┬───────────────┬───────────────┬────┘ │ │ │ ┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐ │ Account │ │ Account │ │ Account │ │ (exchange)│ │ (exchange)│ │ (custody) │ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │ │ │ └───────────────┴───────────────┘ │ Users (one or more, roles per account) ``` Gemini provisions your top-level group during onboarding. After provisioning, administrators create new accounts via API using a Master key. ## Account types `POST /v1/account/create` accepts two types: | Type | What it's for | |------|---------------| | `exchange` | Spot trading, prediction markets, and fund management. Default when `type` is omitted. | | `custody` | Cold-storage custody of crypto assets. Subject to custody fee schedule. See [Fund Management](/trading/rest-api/fund-management) for custody-specific operations. **Custody must be enabled on your group by Gemini before you can create custody accounts — contact the Gemini team.** | For accounts with specialized capabilities (derivatives, margin), create the account through [API settings](https://exchange.gemini.com/settings/api) or contact the Gemini team. The response returns a kebab-cased `account` shortname (e.g., `my-secondary-account`) that you pass as the `account` parameter on subsequent Master-key requests. ## How API keys work with the hierarchy API keys come in two flavors: - **Master keys** are prefixed `master-`. They can act on any account in the group by passing an `account` parameter (the shortname returned by `createNewAccount`) in the request payload. See [Master API Key](/authentication/api-key#master-api-key) for the full signing mechanics. - **Account keys** are prefixed `account-`. They are scoped to a single account and never carry an `account` parameter. The decoded payload for a Master key targeting `my-secondary-account` looks like this — `account` is just another field alongside `request` and `nonce`: ```json { "request": "/v1/balances", "nonce": 1234567890123, "account": "my-secondary-account" } ``` Omit `account` and the request targets whichever account the key itself belongs to. Roles are assigned per key. The four public roles — Administrator, Trader, Fund Manager, Auditor — are documented on the [Roles](/roles#roles) page along with which endpoints each role can access. Specialized roles exist for institutional setups (for example, separate transfer-only and settlement workflows); contact the Gemini team if you need one of these enabled. ## When to use subaccounts ### Organize by purpose Keep different goals, clients, or stakeholders in separate accounts so attribution, audit, and offboarding are clean by construction — not solved retroactively. - **Trading desk** — one account per trader with a scoped API key; ops holds the Master key for funding and reconciliation. - **End-client platforms** — each external client gets their own account; attribution, audit, and offboarding are clean by default. - **Prop vs client books** — client assets in a custody account, prop trading in exchange accounts; same Master key, no commingling. - **Per-LP isolation** — one account per LP for clean P&L attribution and per-entity tax reporting. ### Separate strategies and contain risk Active trades and long-term holdings no longer have to share the same account. Test a new approach in a capped account without touching your main book. - **Long-term vs active** — long-term holdings in one account, active trading in another, rebalanced via internal transfer. - **Experiment quarantine** — fund a small account with a capped transfer; if the strategy blows up, the rest of your books are untouched. ### Give each bot its own space Each agent or bot gets a dedicated account — its own balances, its own rate limits, its own blast radius. One bot going wrong can't affect the others. - **Agentic and automated trading** — one account per agent or bot; a runaway agent's blast radius is contained to its own account and orders. - **Prediction market bots** — one account per bot or market category (crypto, sports, weather); a bad fill or unexpected settlement is contained to that account alone. ## Subaccount patterns ### Agentic and automated trading Give each agent — prediction market bot, strategy bot, LLM-driven trader, market maker — its own account and its own **account-level API key** scoped to that account with only the Trader role. The orchestrator (your control plane) holds the **Master key** with Administrator + Fund Manager roles for provisioning and funding; individual agents never hold the Master key and never have visibility into other accounts. ``` Group ── pm-crypto (exchange) ← account key, Trader role only ├─ pm-sports (exchange) ← account key, Trader role only ├─ agent-trend (exchange) ← account key, Trader role only └─ agent-mm (exchange) ← account key, Trader role only Master key held by orchestrator (Administrator + Fund Manager) ``` **Isolation guarantees per account:** - **Cancel-all scope**: cancels only that account's open orders — other agents are unaffected. - **Rate limits**: enforced per account — one agent hitting its limit does not throttle others. - **Blast radius**: a rogue or crashing agent can only affect its own balances and orders. For additional containment, provision each agent's key with **Requires Heartbeat** (Cancel on Disconnect): if the agent process crashes or goes silent, all its open orders cancel automatically within 30 seconds. See [API Key — Require Heartbeat](/authentication/api-key#require-heartbeat). Endpoints: [Create New Account](/rest-api/common/admin/create-new-account) · [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts) · [Create New Order](/trading/rest-api/orders/create-new-order). ### Prediction market bots Prediction market accounts are exchange accounts — the same account type, the same `account` parameter on Master-key requests, the same isolation guarantees. Organize by market category, by contract type, or by strategy. Fund each account via internal transfer; if one bot takes an unexpected loss on a contract settlement, only that account's balance is affected. ``` Group ── pm-crypto (exchange) ← account key, Trader role only ├─ pm-sports (exchange) ← account key, Trader role only └─ pm-weather (exchange) ← account key, Trader role only Master key held by orchestrator (Administrator + Fund Manager) ``` Each bot authenticates over WebSocket using its account-level key. Before any account in the group can trade, accept the prediction markets terms once at the group level (`POST /v1/prediction-markets/terms/accept`) — a single acceptance covers all accounts in the group. Endpoints: [Create New Account](/rest-api/common/admin/create-new-account) · [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts) · [Prediction Markets](/prediction-markets/prediction-markets). ### Prop vs experimental A production account runs the live book. A small experimental account, funded by a capped internal transfer, runs new strategies. If the experiment blows up, the loss is bounded. ``` Group ── prod └─ experimental (capped via transfer) ``` Endpoints: [Create New Account](/rest-api/common/admin/create-new-account) · [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts). ### Team separation One account per trader. Each trader gets their own account-level API key with the Trader role. The Master key (held by ops) handles funding via internal transfer and uses [List Accounts In Group](/rest-api/common/admin/list-accounts-in-group) for daily reconciliation. ``` Group ── trader-alex ├─ trader-blake └─ trader-casey ``` Endpoints: [Create New Account](/rest-api/common/admin/create-new-account) · [List Accounts In Group](/rest-api/common/admin/list-accounts-in-group) · [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts). ### Client vs proprietary funds Client cold-storage assets live in a custody account; prop trading runs in one or more exchange accounts. Custody and exchange accounts in the same group support internal transfers when assets need to move between books. ``` Group ── client-custody (custody) ├─ prop-trading-1 (exchange) └─ prop-trading-2 (exchange) ``` Endpoints: [Create New Account](/rest-api/common/admin/create-new-account) (use `type: custody` for the custody account; requires prior enablement by the Gemini team) · [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts). ### End-client platform You're building a product on top of Gemini for outside users — managed-account services, fund-of-funds infrastructure, per-LP attribution for a fund, or custody for a wealth platform. Each client or LP gets its own account so attribution, audit, and offboarding are clean by construction rather than solved by partitioned ledgers. ``` Group ── client-alpha (exchange or custody) ├─ client-beta (exchange or custody) └─ client-gamma (exchange or custody) ``` Endpoints: [Create New Account](/rest-api/common/admin/create-new-account) · [List Accounts In Group](/rest-api/common/admin/list-accounts-in-group) · [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts). ### Treasury split Long-term holdings sit in a custody or quiet exchange account; a smaller hot-trading account funds day-to-day execution. Periodic internal transfers rebalance the two. ``` Group ── treasury └─ trading-hot ``` Endpoints: [Create New Account](/rest-api/common/admin/create-new-account) · [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts). ## End-to-end workflow > **Prerequisite:** Subaccounts require an institutional account group provisioned by Gemini during onboarding. Standard individual accounts don't have this structure — if you don't see a Master key option at [API settings](https://exchange.gemini.com/settings/api), contact the Gemini team. 1. **Provision a Master API key.** From the Gemini Exchange [API settings](https://exchange.gemini.com/settings/api), create a Master-level key with the Administrator role. 2. **Create the account.** Call [`POST /v1/account/create`](/rest-api/common/admin/create-new-account) with a unique `name` and `type` (`exchange` or `custody`). Exchange accounts are available immediately. Custody accounts require prior enablement by the Gemini team. The response returns a kebab-cased shortname; record it. **Tip:** choose a descriptive shortname like `agent-trend` or `client-alpha` — it's what you'll pass in every Master-key request targeting that account. 3. **List your group.** Call [`POST /v1/account/list`](/rest-api/common/admin/list-accounts-in-group) to confirm the new account is present and to capture all shortnames. 4. **Fund the account.** Call [`POST /v1/account/transfer/{currency}`](/trading/rest-api/fund-management/transfer-between-accounts) to move balances between accounts in the same group. 5. **Place orders against the account.** Use your Master key with the `account` parameter set to the shortname, or provision an account-level key for that account. 6. **Rename when needed.** Call [`POST /v1/account/rename`](/rest-api/common/admin/rename-account) to change the display name or shortname (the shortname is what you'll use in subsequent requests). 7. **Reconcile.** Use [`POST /v1/balances`](/trading/rest-api/fund-management/get-available-balances), [`POST /v2/transfers`](/trading/rest-api/fund-management/list-past-transfers), and [`POST /v1/transactions`](/trading/rest-api/fund-management/get-transaction-history) per account or across the group. ## Constraints and gotchas - **Max accounts per group.** The group-level account cap is configurable by Gemini; contact the Gemini team if you need it raised. - **Account listing limit.** [List Accounts In Group](/rest-api/common/admin/list-accounts-in-group) returns up to 500 accounts per call. - **Same-group transfers only.** [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts) moves funds between two accounts in the same group. There is no API path to transfer between groups. - **Use shortnames, not display names.** The `account` parameter on Master-key requests, and the value returned by [Create New Account](/rest-api/common/admin/create-new-account), is the kebab-cased shortname. - **Roles are per key.** A Master key needs the Administrator role to create accounts; the Fund Manager role to execute internal transfers; the Trader role to place orders. See [Roles](/roles#roles) for the full matrix. - **Custody requires prior enablement.** Custody must be enabled on your group by Gemini before `POST /v1/account/create` will accept `type: custody`. Contact the Gemini team to get it enabled. Once enabled, custody accounts have a different fee schedule and do not support open trading. See [Fund Management](/trading/rest-api/fund-management) for custody-specific behavior. - **Derivatives and margin accounts.** Accounts with derivatives or margin capabilities are not created via this endpoint — provision them through [API settings](https://exchange.gemini.com/settings/api) or via the Gemini team. - **OAuth.** Listing accounts via OAuth requires the `account:read` scope. See [OAuth Scopes](/authentication/oauth#oauth-scopes). ## Related - [Master API Key](/authentication/api-key#master-api-key) — how the `account` payload parameter works - [Roles](/roles#roles) — what each role can do on Master and account-scoped endpoints - [Create New Account](/rest-api/common/admin/create-new-account) · [Rename Account](/rest-api/common/admin/rename-account) · [List Accounts In Group](/rest-api/common/admin/list-accounts-in-group) · [Get Account Detail](/rest-api/common/admin/get-account-detail) - [Transfer Between Accounts](/trading/rest-api/fund-management/transfer-between-accounts) - [Prediction Markets](/prediction-markets/prediction-markets) — prediction market bots use exchange accounts; terms acceptance is once per group - [OAuth Scopes](/authentication/oauth#oauth-scopes) --- URL: https://developer.gemini.com/rest-api/common/admin/roles-endpoint.md # Roles Endpoint The v1/roles endpoint will return a string of the role of the current API key. The response fields will be different for account-level and master-level API keys.} example={{ request: { method: "POST", url: "https://api.gemini.com/v1/roles", headers: [ { name: "X-GEMINI-APIKEY", value: "" }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/roles", nonce: "", }, }, }} sections={[ { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/admin/rename-account.md # Rename Account " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/account/rename", nonce: "", account: "my-exchange-account", newName: "My Exchange Account New Name", newAccount: "my-exchange-account-new-name", }, }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint can be either a Master or Account level API key and must have the Administrator role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/admin/list-accounts-in-group.md # List Accounts in Group " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/account/list", nonce: "", limit_accounts: 100, timestamp: 1632485834721, }, }, }} sections={[ { heading: "Roles", children: ( <>

    The API key you use to access this endpoint must be a Master level key. See Roles for more information.

    The OAuth scope must have account:read assigned to access this endpoint. See OAuth Scopes for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/admin/get-account-detail.md # Get Account Detail " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/account", nonce: "", account: "primary", }, }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint can be either a Master or Account level key with any role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/rest-api/common/admin/create-new-account.md # Create New Account " }, { name: "X-GEMINI-PAYLOAD", value: "" }, { name: "X-GEMINI-SIGNATURE", value: "" }, ], body: { request: "/v1/account/create", nonce: "", name: "My Secondary Account", type: "exchange", }, }, }} sections={[ { heading: "Roles", children: (

    The API key you use to access this endpoint must be a Master level key and have the Administrator role assigned. See Roles for more information.

    ), }, { heading: "Headers", children: , }, { heading: "Request Body", children: , }, { heading: "Responses", children: , }, ]} /> --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/when-can-sequence-numbers-reset.md # When can sequence numbers be reset? Gemini runs the server side of the FIX connection ("acceptor"). Gemini never resets sequence numbers on the server side during the logon workflow unless the client explicitly requests it. The client ("initiator") can reset sequence numbers during [Logon ``](/trading/fix/overview/session-level-messages/logon) by setting [ResetSeqNumFlag `<141>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_141.html) to `Y`. Gemini recommends that client consider configuring the FIX initiator to automatically reset sequence numbers under the following conditions: - logon - logout - disconnect - error While synchronizing sequence numbers after a replay, the client may send a [Sequence Reset `<4>`](/trading/fix/overview/session-level-messages/sequence-reset) with [GapFillFlag `<123>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_123.html) = `Y` in lieu of a replay. --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/test-request.md # Test Request <1> # The [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) message forces a heartbeat from the opposing application. The [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) message checks sequence numbers or verifies communication line status. The opposite application responds to the [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) with a [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html) containing the [TestReqID `<112>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_112.html). The [TestReqID `<112>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_112.html) verifies that the opposite application is generating the heartbeat as the result of [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) and not a normal timeout. The opposite application includes the [TestReqID `<112>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_112.html) in the resulting [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html). Any string can be used as the [TestReqID `<112>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_112.html) (one suggestion is to use a timestamp string). --- ### FIELDS | Tag | Name | Req | Description | | --- | --------------------------------------------------------------------- | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `1` | | 112 | [TestReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_112.html) | Y | Identifier included in [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) message to be returned in resulting [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html) | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/sequence-reset.md # Sequence Reset <4> # The [Sequence Reset `<4>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_4_4.html) message is used in response to a [Resend Request `<2>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_2_2.html) message when one or more messages must be skipped over for the following reasons: - During normal resend processing, the sending application may choose not to send a message (e.g. an aged order). - During normal resend processing, a number of administrative messages are skipped and not resent (such as [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html) and [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html)). Gemini does not support Reset mode ([GapFillFlag `<123>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_123.html) not present or equal to `N`). --- ### FIELDS
    Valid value:
    `Y` = Gap Fill message", ], ["", "[Standard Trailer](/trading/fix/overview/standard-trailer)", "Y", ""], ]} /> --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/resend-request.md # Resend Request <2> # Send a Resend Request to ask the counterparty to retransmit messages. Use this message when you detect a sequence gap, lose a message, or initialize a session. You can request a single message, a message range, or all messages after a specific sequence number. :::warning Consider message type before resending. If market conditions changed since an order was created, do not retransmit stale orders. Use [Sequence Reset `<4>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_4_4.html) (Gap Fill) to skip unneeded messages. ::: :::warning You must process messages in sequential order. If you miss message 7 and receive messages 8–9, ignore 8 and 9 and request a resend starting at 7 (`BeginSeqNo=7`, `EndSeqNo=0`). Setting `EndSeqNo=0` (infinity) resolves sequence gaps faster during simultaneous recoveries. ::: - To request a single message: [BeginSeqNo `<7>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_7.html) = [EndSeqNo `<16>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_16.html) - To request a range of messages: [BeginSeqNo `<7>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_7.html) = first message of range, [EndSeqNo `<16>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_16.html) = last message of range - To request all messages after a specific sequence number: [BeginSeqNo `<7>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_7.html) = starting sequence number, [EndSeqNo `<16>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_16.html) = `0` (represents infinity). --- ### FIELDS | Tag | Name | Req | Description | | --- | -------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `2` | | 7 | [BeginSeqNo](https://www.onixs.biz/fix-dictionary/4.4/tagNum_7.html) | Y | | | 16 | [EndSeqNo](https://www.onixs.biz/fix-dictionary/4.4/tagNum_16.html) | Y | | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/reject.md # Reject <3> # Gemini sends a [Reject `<3>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_3_3.html) message when a message is received but cannot be properly processed due to a session-level rule violation. A reject is typically a serious error in the trading application's session logic. A session reject would also be generated if client message rate has exceeded the allocated throttle. --- ### FIELDS | Tag | Name | Req | Description | | --- | ------------------------------------------------------------------------------- | --- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `3` | | 45 | [RefSeqNum](https://www.onixs.biz/fix-dictionary/4.4/tagNum_45.html) | Y | [MsgSeqNum `<34>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_34.html) of the rejected message. | | 58 | [Text](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) | N | Explanation for rejection | | 371 | [RefTagID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_371.html) | N | The tag number of the FIX field being referenced. | | 372 | [RefMsgType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_372.html) | N | The [MsgType `<35>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_35.html) of the rejected message. | | 373 | [SessionRejectReason](https://www.onixs.biz/fix-dictionary/4.4/tagNum_373.html) | N | Code to identify the reason for the [Reject `<3>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_3_3.html) message.

    Valid values:
    `0` = Invalid tag number
    `1` = Required tag missing
    `2` = Tag not defined for this message type
    `3` = Undefined Tag
    `4` = Tag specified without a value
    `5` = Value is incorrect (out of range) for this tag
    `6` = Incorrect data format for value
    `10` = [SendingTime `<52>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_52.html) accuracy problem
    `11` = Invalid [MsgType `<35>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_35.html)
    `99` = Other | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/logout.md # Logout <5> # The [Logout `<5>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_5_5.html) message initiates or confirms the termination of a FIX session. --- ### FIELDS | Tag | Name | Req | Description | | --- | --------------------------------------------------------------- | --- | ------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `5` | | 58 | [Text](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) | N | Reason for logging out. | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/logon.md # Logon
    # The [Logon ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_A_65.html) message must be the first message sent by the application requesting to initiate a FIX session. The [Logon ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_A_65.html) message authenticates an institution establishing a connection to Gemini. Upon receipt of a [Logon ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_A_65.html) message, Gemini will authenticate the institution requesting connection by validating the source IP Address, [SenderCompID `<49>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_49.html), and [TargetCompID `<56>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_56.html) identifying the institution. The server will then issue a [Logon ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_A_65.html) message as acknowledgment that the connection request has been accepted. The acknowledgment [Logon ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_A_65.html) can also be used by the institution to validate that the connection was established with the correct party. If validation fails, the connection will be dropped without a [Reject `<3>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_3_3.html). --- ### FIELDS | Tag | Name | Req | Description | | ---- | --------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `A` | | 141 | [ResetSeqNumFlag](https://www.onixs.biz/fix-dictionary/4.4/tagNum_141.html) | N | Should be set to `Y` when the client wants to indicate that both sides of the FIX session should reset sequence numbers. | | 98 | [EncryptMethod](https://www.onixs.biz/fix-dictionary/4.4/tagNum_98.html) | Y | Gemini does not support encryption.

    **Valid value:**
    `0 = None` | | 108 | [HeartBtInt](https://www.onixs.biz/fix-dictionary/4.4/tagNum_108.html) | Y | Heartbeat interval in seconds.

    **Valid value:**
    `30 = 30 seconds` | | 9001 | [CancelOnDisconnect](/trading/fix/overview/dictionary/custom-tags) | N | Only used for [Order Entry](/trading/fix/overview/dictionary/version). When present and **true**, orders will be cancelled on disconnect. When present and **false**, orders will not be cancelled on disconnect. When absent, orders will not be cancelled on disconnect.

    **Valid values:**
    `Y = Enable cancel on disconnect for this session`
    `N = Disable cancel on disconnect for this session` | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/heartbeat.md # Heartbeat <0> # The [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html) monitors the status of the communication link and identifies when the last of a string of messages was not received. _The only supported heartbeat interval, as specified by the [Logon `
    `](https://www.onixs.biz/fix-dictionary/4.4/msgType_A_65.html) message, is 30 seconds_. When either end of a FIX connection has not sent any data for [HeartBtInt `<108>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_108.html) seconds, it will transmit a [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html) message. When either end of the connection has not received any data for ([HeartBtInt `<108>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_108.html) + "some reasonable transmission time") seconds, it will transmit a [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) message. If there is still no [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html) message received after ([HeartBtInt `<108>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_108.html) + "some reasonable transmission time") seconds then the connection should be considered lost and corrective action be initiated. Note that a [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) message can still be sent independent of the value of the [HeartBtInt `<108>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_108.html), which will force a [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html) message. Heartbeats issued as the result of [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) must contain the [TestReqID `<112>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_112.html) transmitted in the [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) message. This is useful to verify that the [Heartbeat `<0>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_0_0.html) is the result of the [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) and not as the result of a regular timeout. --- ### FIELDS | Tag | Name | Req | Description | | --- | --------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------ | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `0` | | 112 | [TestReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_112.html) | N\* | Required when the heartbeat is the result of a [Test Request `<1>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_1_1.html) | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/establishing-a-connection.md # Establishing a connection 1. Client sends server → [Logon ``](/trading/fix/overview/session-level-messages/logon) message 2. Is client [ResetSeqNumFlag `<141>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_141.html) set to `Y`? - **Yes**, reset sequence numbers and proceed: - Server resets the next expected client sequence number - Server resets its own sequence number - Server responds with ← [Logon ``](/trading/fix/overview/session-level-messages/logon) message with reset sequence number - Client sends server → [Heartbeat `<0>`](/trading/fix/overview/session-level-messages/heartbeat) - **No**, negotiate sequence numbers on both sides: 1. Is client [MsgSeqNum `<34>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_34.html) value what the server is expecting? - **No**, value is greater than expected: - Server responds with ← [Logon ``](/trading/fix/overview/session-level-messages/logon) message - Server sends client ← [Resend Request `<2>`](/trading/fix/overview/session-level-messages/resend-request) - Client sends server → [Sequence Reset `<4>`](/trading/fix/overview/session-level-messages/sequence-reset) ([GapFillFlag `<123>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_123.html) = `Y`) - **No**, value is less than expected: - Server disconnects - After checking what happened, client re-sends [Logon ``](/trading/fix/overview/session-level-messages/logon) message with [ResetSeqNumFlag `<141>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_141.html) field set to `Y` - **Otherwise**: - server sends client ← [Heartbeat `<0>`](/trading/fix/overview/session-level-messages/heartbeat) 2. Is server [MsgSeqNum `<34>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_34.html) value what the client is expecting? - **No**, value is greater than expected: - Server responds with ← [Logon ``](/trading/fix/overview/session-level-messages/logon) message - Client sends server → [Resend Request `<2>`](/trading/fix/overview/session-level-messages/resend-request) - If the server is unable to replay the messages due to technical reasons, it may send a [Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgtype_j_106.html) message with: - [BusinessRejectReason `<380>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_380.html) set to `0` (Other) - [Text `<58>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) set to `"Unable to process ResendRequest. Please contact support."` - If there are no issues, the server proceeds to replay the messages to the client: - Server responds with ← message [PossDupFlag `<43>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_43.html) = `Y` - Replay from server to client is complete - Server responds with ← [Heartbeat `<0>`](/trading/fix/overview/session-level-messages/heartbeat) - **No**, value is less than expected: - Client disconnects or sends server → [Logout `<5>`](/trading/fix/overview/session-level-messages/logout) - Gemini coordinates with the client to triage so the FIX connection can be re-established - **Otherwise**: - Client sends server → [Heartbeat `<0>`](/trading/fix/overview/session-level-messages/heartbeat) 3. Success! Your FIX connection is established. --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/ending-a-connection.md # Ending a connection The client may send the server an optional [Logout `<5>`](/trading/fix/overview/session-level-messages/logout) message but the exchange will not interpret its absence as being an abnormal condition. Under certain conditions, the server may send the client a [Logout `<5>`](/trading/fix/overview/session-level-messages/logout) message where the [Text `<58>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) field contains the reason, such as scheduled maintenance. --- URL: https://developer.gemini.com/trading/fix/overview/session-level-messages/business-message-reject.md # Business Message Reject # Gemini sends [Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html) when the exchange receives a valid FIX message which cannot be processed. Examples include: - receiving a market data request on an order entry channel, or vice versa Gemini does not use [Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html) to handle invalid [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) messages. Rejected orders are handled with an [Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) message with an [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) field with a value of `8 - Rejected`. --- ### FIELDS | Tag | Name | Req | Description | | --- | -------------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `j` | | 45 | [RefSeqNum](https://www.onixs.biz/fix-dictionary/4.4/tagNum_45.html) | Y | [MsgSeqNum `<34>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_34.html) of the rejected message. | | 58 | [Text](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) | N | Explanation for rejection. | | 372 | [RefMsgType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_372.html) | N | [MsgType `<35>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_35.html) of the rejected message. | | 379 | [BusinessRejectRefID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_379.html) | N | The value of the business level ID field being referenced. | | 380 | [BusinessRejectReason](https://www.onixs.biz/fix-dictionary/4.4/tagNum_380.html) | Y | Code to identify the reason for the [Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html) message.

    Valid values:
    `0` = Other
    `1` = Unknown ID
    `2` = Unknown Security
    `3` = Unsupported Message Type
    `4` = Application not available
    `5` = Conditionally Required Field Missing | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/overview/dictionary/version.md # Version Gemini uses [FIX 4.4](https://www.fixtrading.org/standards/fix-4-4/) with the 20030618 errata. Of particular note if you may be using an older dictionary: --- URL: https://developer.gemini.com/trading/fix/overview/dictionary/download.md # Download For your convenience, Gemini maintains our custom dictionary in [QuickFIX](https://quickfixengine.org/) XML form: | Environment | File | | ----------- | ------------------------------------------------------------------- | | Production | https://docs.gemini.com/files/gemini-fix-dictionary.xml.zip | | Sandbox | https://docs.sandbox.gemini.com/files/gemini-fix-dictionary.xml.zip | --- URL: https://developer.gemini.com/trading/fix/overview/dictionary/custom-tags.md # Custom tags ### TAGS AND FIELDS
    Tag Field Type Notes
    9000 `RiskLiquidityFlag` Boolean Used in [NewOrderSingle <D>](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit). Indicates whether or not the order should match against Liquidation Orders sent from the Liquidation Engine. Only allowed from permissioned Market Makers.
    9001 `CancelOnDisconnect` Boolean Used in [Logon <A>](/trading/fix/overview/session-level-messages/logon) to enable or disable session-level cancel on disconnect.
    9002 `MDEntryMakerSide` Char Used in [Market Data - Incremental Refresh <X>](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) MDEntry groups when [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) has the value `2 = Trade` to indicate the maker side of a trade. See [Examples: Request to enable maker side on trades](/trading/fix/market-data/examples/market-data-requests#enable-maker-side-on-trades).
    9003 `EnableMDEntryMakerSide` Boolean Used in [Market Data Request <V>](/trading/fix/market-data/exchange-bound-messages/market-data-request) to optionally enable showing custom field 9002 `MDEntryMakerSide` in [Market Data - Incremental Refresh <X>](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) messages when [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) has the value `2 = Trade`. See [Examples: Showing Maker Side For Trades](/trading/fix/market-data/examples/market-data-responses#showing-maker-side).
    9009 `MDEntryFundingIsRealized` Boolean Used in [Market Data - Incremental Refresh <X>](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) MDEntry groups when [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) has the value `S = Funding Amount` to indicate the IsRealized field of Funding Amount.
    9008 `EventId` Int Used in [Market Data - Incremental Refresh <X>](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) to specify the event associated with the update. This is for information only and may not be used to form a business logic. See [Examples: market data responses sent to clients](/trading/fix/market-data/examples/market-data-responses).
    7777 `EventOutcome` Char Used in [NewOrderSingle <D>](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit) to indicate the event outcome for prediction markets orders. Accepted values are `0` or `1` indicating the `YES` or `NO` outcomes, respectively.
    --- URL: https://developer.gemini.com/trading/fix/order-entry/workflow/when-trades-occur.md # When trades occur - In the event of a **Partial Fill**, Gemini sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) with: - [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) set to `F = Trade` - [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) set to `1 = Partially filled` - When the order is **Filled**, Gemini sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) with: - [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) set to `F = Trade` - [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) set to `2 = Filled` --- URL: https://developer.gemini.com/trading/fix/order-entry/workflow/submitting-an-order.md # Submitting an order **Client → Server** - Sends either a [New Order Single Limit ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit) message **or** a [New Order Single Market ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit) message --- ### Does Gemini accept the order? 1. **Yes: Order is accepted for initial processing** - **Server → Client**: Sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) for a new order with: - [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) set to `0 = New` - [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) set to `0 = New` **Is the order marketable?** - **Yes**: The server executes one or more initial fills - **Server → Client**: Sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) for each [fill](/trading/fix/order-entry/examples/execution-reports#fill) or [partial fill](/trading/fix/order-entry/examples/execution-reports#partial-fill) - **Does the order have remaining quantity?** - **Yes**: The server places the remaining quantity on the order book - **No**: The server closes the order - **No**: The server puts the entire quantity of the order on the book 2. **No: Order is rejected** - **Server → Client**: Sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) indicating the order was rejected with: - [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) set to `8 = Rejected` - [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) set to `8 = Rejected` 3. **No: Throttle exceeded** - **Server → Client**: Sends a [Session Reject `<3>`](/trading/fix/overview/session-level-messages/reject) indicating the message was rejected due to a rate limit breach --- URL: https://developer.gemini.com/trading/fix/order-entry/workflow/submitting-a-stop-limit-order.md # Submitting a stop limit order **Client → Server** - Sends a [New Order Single Stop Limit ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit) message with: - [OrdType `<40>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) set to `4 = Stop Limit` - [StopPx `<99>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_99.html) value that is: - **less than or equal** to [Price `<44>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html) on [Side `<54>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) = `1 (Buy)`, **or** - **greater than or equal** to [Price `<44>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html) on [Side `<54>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) = `2 (Sell)` --- ### Does Gemini accept the order? 1. **Yes: Order is accepted for initial processing** - **Server → Client**: Sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) for a new order with: - [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) set to `0 = New` - [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) set to `0 = New` - [StopPx `<99>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_99.html) set to the same `StopPx` value provided on entry **Has a trade occurred that would trigger your stop order?** - **Yes**: The stop order triggers - **Server → Client**: Sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) message with the child limit order details - **No**: The stop order remains hidden and resting until triggered by a qualifying trade, or until cancelled **Is the limit order marketable?** - **Yes**: The server executes one or more initial fills - **Server → Client**: Sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) for each [fill](/trading/fix/order-entry/examples/execution-reports#fill) or [partial fill](/trading/fix/order-entry/examples/execution-reports#partial-fill) - **Does the order have remaining quantity?** - **Yes**: The server puts the remaining quantity on the order book - **No**: The server closes the order - **No**: The server puts the entire quantity of the order on the book 2. **No: Order is rejected** - **Server → Client**: Sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) indicating the order was rejected with: - [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) set to `8 = Rejected` - [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) set to `8 = Rejected` --- URL: https://developer.gemini.com/trading/fix/order-entry/workflow/cancelling-an-order.md # Cancelling an order **Client → Server** - Sends an [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request) message --- ### Outcome 1. **If Successful** - **Server → Client**: Sends an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) with: - [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) set to `4 = Canceled` - [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) set to `4 = Canceled` 2. **If Unsuccessful** - **Server → Client**: Sends an [Order Cancel Reject `<9>`](/trading/fix/order-entry/client-bound-messages/order-cancel-reject) explaining why the cancel request could not be fulfilled. --- URL: https://developer.gemini.com/trading/fix/order-entry/price-&-quantity/understanding-price-&-quantity.md # Understanding price and quantity ## New Order Single Example In a [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit) message for the `BTCUSD` symbol: - [OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html) is denominated in **BTC** (the quantity currency). - [Price `<44>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html) is denominated in **USD** (the price currency). --- ### Raw FIX Message ```txt RAW 8=FIX.4.4|9=137|35=D|34=2237|49=CLIENT|52=20160218-22:25:24.277|56=GEMINI|11=SOME_ORDER|38=5|40=2|44=420.18|54=1|55=BTCUSD|59=1|60=20160218-17:24:14.453|10=066| HEADER 8 BeginString: FIX4.4 9 BodyLength: 137 34 MsgSeqNum: 2237 35 MsgType: NewOrderSingle (D) 49 SenderCompID: CLIENT 52 SendingTime: 20160218-22:25:24.277 56 TargetCompID: GEMINI BODY 11 ClOrdID: SOME_ORDER 38 OrderQty: 5 40 OrdType: LIMIT (2) 44 Price: 420.18 54 Side: BUY (1) 55 Symbol: BTCUSD 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20160218-17:24:14.453 TRAILER 10 CheckSum: 066 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/price-&-quantity/currency-denominated-fields.md # Currency-denominated fields --- ### Fields `](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit)", "[Price `<44>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html)", "[OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html)\n\n[MinQty `<110>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_110.html)", ], [ "[Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request)", "—", "[OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html)", ], [ "[Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report)", "[Price `<44>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html)\n\n[AvgPx `<6>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_6.html)\n\n[LastPx `<31>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_31.html)", "[OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html)\n\n[CumQty `<14>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html)\n\n[LeavesQty `<151>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html)\n\n[LastQty `<32>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_32.html)", ], [ "[IOI `<6>`](https://www.onixs.biz/fix-dictionary/4.4/msgtype_6_6.html)", "[StipulationValue `<234>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_234.html) when [StipulationType `<233>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_233.html) is `PRICE`", "[IOIQty `<27>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_27.html)\n\n[StipulationValue `<234>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_234.html) when [StipulationType `<233>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_233.html) is `MINQTY`", ], ]} /> --- ### Fees The amount in [Commission `<12>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_12.html) is denominated in the currency specified by the [currency code](https://www.onixs.biz/fix-dictionary/4.4/app_6_a.html) value in [CommCurrency `<479>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_479.html). --- URL: https://developer.gemini.com/trading/fix/order-entry/identifiers/identifiers-assigned-by-gemini.md # Identifiers assigned by Gemini `](/trading/fix/order-entry/client-bound-messages/execution-report)\n[Order Cancel Reject `<9>`](/trading/fix/order-entry/client-bound-messages/order-cancel-reject)", "Globally unique **order identifier** assigned by Gemini, which remains constant throughout the order's lifecycle.\n\n \u00A0 \n\nWhen [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) = `0 = New` and [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) = `0 = New`, the order is considered **on the book**.\n\n \u00A0 \n\nFor an order cancel reject, the value is `NONE`.", ], [ "17", "[ExecID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_17.html)", "[Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report)", "Globally unique **event identifier** assigned by Gemini. This ID can refer to:\n\n- Order accepted id (when the order is placed on the book)\n- Trade id (partial fill or fill)\n- Order cancellation id\n- Order rejection id", ], ]} /> --- URL: https://developer.gemini.com/trading/fix/order-entry/identifiers/client-supplied-identifiers.md # Client supplied identifiers `](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit)\n- [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request)", "- [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report)\n- [Order Cancel Reject `<9>`](/trading/fix/order-entry/client-bound-messages/order-cancel-reject)", "The client's request identifier, which may refer to either an order or an order-cancel request.\n\n \u00A0 \n\nGemini **strongly recommends** making this field **unique** among active orders for a given port. One common practice is to include a date prefix (e.g., `20160114_0001`).\n\n \u00A0 \n\nGemini **does not enforce** uniqueness, so sending a duplicate [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) might cause confusion in downstream reports.", ], [ "41", "[OrigClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_41.html)", "- [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit)\n- [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request)", "- [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report)\n- [Order Cancel Reject `<9>`](/trading/fix/order-entry/client-bound-messages/order-cancel-reject)", "Specifies the [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) of the order you want to cancel. This must correspond to a **currently outstanding order** submitted during this trading session.\n\n \u00A0 \n\nGemini will cancel **every** active order whose [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) matches the value in this field.", ], ]} /> --- URL: https://developer.gemini.com/trading/fix/order-entry/gemini-clearing/initiate-a-clearing-order.md # Initiate a Clearing order ## New Order Cross `` To submit a new Gemini Clearing order, send a [New Order Cross ``](https://www.onixs.biz/fix-dictionary/4.4/msgtype_s_115.html) message with [OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) Limit. Gemini will respond to a [New Order Cross ``](https://www.onixs.biz/fix-dictionary/4.4/msgtype_s_115.html) with an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report). The [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) will have [OrderID `<37>`](https://www.onixs.biz/fix-dictionary/4.4/tagnum_37.html) populated with the `clearing_id` which the counterparty will need to confirm the order. --- ### Fields | Tag | Name | Req | Description | | --- | ------------------------------------------------------------------------------- | --- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `s` | | 11 | [ClOrdId](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) | Y | Unique identifier of the cross order as assigned by the institution. Uniqueness must be guaranteed by the institution for the duration of the connection to the order entry channel. | | 38 | [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html) | Y | Decimal quantity of quantity currency to buy or sell | | 40 | [OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) | Y | Order type.

    Valid values:
    `2 = Limit` | | 44 | [Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html) | Y | Decimal price. Price is denominated in `CCY1`. | | 54 | [Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) | Y | Side of the initiator of the order.

    Valid values:
    `1 = Buy`
    `2 = Sell` | | 55 | [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) | Y | Ticker symbol of the order.

    See [Supported Symbols](/market-data/symbols-and-minimums) for valid values. | | 60 | [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html) | Y | Time of order creation (expressed in UTC). | | 126 | [ExpireTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_126.html) | Y | Time of order expiration (expressed in UTC). Can be up to 30 days from order initiation. | | 548 | [CrossID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_548.html) | Y | Unique identifier for a cross order | | 549 | [CrossType](https://www.onixs.biz/fix-dictionary/4.4/tagnum_549.html) | Y | Type of cross being submitted.

    Valid values:
    `1 = Cross trade which is either completely executed or not` | | 550 | [CrossPrioritization](https://www.onixs.biz/fix-dictionary/4.4/tagnum_550.html) | Y | Indicates if one side or the other of a cross order should be prioritized.

    Valid values:
    `0 = None` | | 552 | [NoSides](https://www.onixs.biz/fix-dictionary/4.4/tagnum_552.html) | Y | Number of [side `<54>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) repeating group instances

    Valid values:
    `1 = One Side` | | 583 | [ClOrdLinkID](https://www.onixs.biz/fix-dictionary/4.4/tagnum_583.html) | N\* | Also referred to as `counterparty_id`. A value that is assigned to a specific counterparty Gemini account. Can be accessed on the [web interface](https://www.gemini.com/clearing) under the `GEMINI ID` label. See [workflow](/trading/fix/order-entry/workflow/submitting-an-order) for more detail. | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- ### Request ```txt RAW 8=FIX.4.4|9=204|35=s|34=2|49=TESTOE001|52=20190807-19:28:38.078|56=GEMINI|40=2|44=12000|55=BTCUSD|60=20190807-19:28:38.078|126=20190807-21:28:38.078|548=26990504|549=1|550=0|552=1|54=1|11=87749738|38=2.22222|10=023| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 204 35 MsgType: NewOrderCross (s) 34 MsgSeqNum: 2 49 SenderCompID: TESTOE001 52 SendingTime: 20190807-19:28:38.078 56 TargetCompID: GEMINI BODY 40 OrdType: LIMIT (2) 44 Price: 12000 55 Symbol: BTCUSD 60 TransactTime: 20190807-19:28:38.078 126 ExpireTime: 20190807-21:28:38.078 548 CrossID: 26990504 549 CrossType: Cross trade which is either completely executed or not executed at all (1) 550 CrossPrioritization: No priority (0) 552 NoSides: One Side (1) 54 Side: BUY (1) 11 ClOrdID: 87749738 38 OrderQty: 2.22222 TRAILER 10 CheckSum: 023 ``` ### Execution Report ```txt RAW 8=FIX.4.4|9=197|35=8|34=2|49=GEMINI|52=20190807-19:28:38.251|56=TESTOE001|6=12000|11=87749738|14=2.22222|17=1565206118250|37=2DZ4MPQM|39=0|44=12000|54=1|55=BTCUSD|60=20190807-19:28:38.250|150=0|151=2.22222|10=009| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 197 35 MsgType: ExecutionReport (8) 34 MsgSeqNum: 2 49 SenderCompID: GEMINI 52 SendingTime: 20190807-19:28:38.251 56 TargetCompID: TESTOE001 BODY 6 AvgPx: 12000 11 ClOrdID: 87749738 14 CumQty: 2.22222 17 ExecID: 1565206118250 37 OrderID: 2DZ4MPQM 39 OrdStatus: NEW (0) 44 Price: 12000 54 Side: BUY (1) 55 Symbol: BTCUSD 60 TransactTime: 20190807-19:28:38.250 150 ExecType: NEW (0) 151 LeavesQty: 2.22222 TRAILER 10 CheckSum: 009 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/gemini-clearing/gemini-clearing-workflow.md # Gemini Clearing workflow Gemini Clearing allows two parties to settle a trade off the order book. The initiator enters the trade details for any supported [symbol](/market-data/symbols-and-minimums) and generates a `trade_id`. If a `counterparty_id` is supplied, only the specified counterparty can confirm the order. If a counterparty_id is not supplied, the ticket will generate a `trade_id` that can filled by any counterparty. --- ### Bilateral Trade 1. Initiator places a [New Order Cross ``](/trading/fix/order-entry/gemini-clearing/initiate-a-clearing-order) message with the order details 2. Did the initiator use a `counterparty_id`? - Yes, only the Gemini account associated with the `counterparty_id` will be able to complete the order. - No, anybody with the `clearing_id` will be able to confirm the order. 3. The counterparty will be able to confirm the order on the [web interface](https://www.gemini.com/clearing), via [FIX](), or via [REST]() - The order initiator can cancel the order if the counterparty has not yet confirmed. Cancellation can be performed on the [web interface](https://www.gemini.com/clearing), via [FIX](), and [REST](). 4. Once confirmed, Gemini will attempt settlement. Settlement can only occur when both parties have the funds required in their account to fill the trade. - If any party does not post the funds required to settle the trade before the expiration time, then the order will expire. --- ### Broker Trade 1. The broker submits a [New Order Cross ``](/trading/fix/order-entry/gemini-clearing/initiate-a-clearing-order) message with the order details and both `counterparty_id`s of the buyer and seller. 2. The buyer and seller can both review the trade details and confirm the order on the [web interface](https://www.gemini.com/clearing), via [FIX](), or via [REST]() - Either side can cancel the trade if both the buyer and seller have yet to confirm the order. Cancellation can be done via the [web interface](https://www.gemini.com/clearing), [FIX](), and [REST]() - The broker will be able to see the status of the order on the [web interface](https://www.gemini.com/clearing) and over the [/v1/clearing/status](/rest/clearing#get-clearing-order-status) endpoint on the REST API 3. Once confirmed, Gemini will attempt settlement. Settlement can only occur when both parties have the funds required in their account to fill the trade. - If any party does not post the funds required to settle the trade before the expiration time, then the order will expire. --- URL: https://developer.gemini.com/trading/fix/order-entry/gemini-clearing/fix-broker-support.md # FIX broker support Gemini Clearing also allows for brokers to facilitate trades between two Gemini customers. A broker can submit a new Gemini Clearing order that must then be confirmed by each counterparty before settlement. --- ## Broker New Order Cross ### Fields | Tag | Name | Req | Description | | --- | ------------------------------------------------------------------------------- | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `s` | | 40 | [OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) | Y | Order type.

    Valid values:
    `2 = Limit` | | 44 | [Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html) | Y | Decimal price. Price is denominated in `CCY1` | | 55 | [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) | Y | Ticker symbol of the order.

    See [Supported Symbols](/market-data/symbols-and-minimums) for valid values. | | 60 | [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html) | Y | Time of order creation (expressed in UTC). | | 548 | [CrossID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_548.html) | Y | Unique identifier for a cross order | | 549 | [CrossType](https://www.onixs.biz/fix-dictionary/4.4/tagnum_549.html) | Y | Type of cross being submitted.

    Valid values:
    `1 = Cross trade which is either completely executed or not` | | 550 | [CrossPrioritization](https://www.onixs.biz/fix-dictionary/4.4/tagnum_550.html) | Y | Indicates if one side or the other of a cross order should be prioritized.

    Valid values:
    `0 = None` | | 552 | [NoSides](https://www.onixs.biz/fix-dictionary/4.4/tagnum_552.html) | Y | Number of [side `<54>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) repeating group instances

    Valid values:
    `2 = Two Sides` | | 54 | [Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) | Y | **Part of a repeating group**
    Side of the source/target of the order. The opposite repeating group must have the opposite side.

    Valid values:
    `1 = Buy`
    `2 = Sell` | | 11 | [ClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) | Y | **Part of a repeating group**
    Unique identifier of the cross order as assigned by the institution. Uniqueness must be guaranteed by the institution for the duration of the connection to the order entry channel. | | 583 | [ClOrdLinkID](https://www.onixs.biz/fix-dictionary/4.4/tagnum_583.html) | Y | **Part of a repeating group**
    Also referred to as `counterparty_id`. A value that is assigned to a specific counterparty Gemini account. Can be accessed on the [web interface](https://www.gemini.com/clearing) under the `GEMINI ID` label. See [workflow](/trading/fix/order-entry/workflow/submitting-an-order) for more detail.

    Must be consistent with the rest of the repeating group. | | 38 | [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html) | Y | **Part of a repeating group**
    Decimal quantity of quantity currency to buy or sell.

    Must match the opposing repeating group. | | 126 | [ExpireTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_126.html) | Y | Time of order expiration (expressed in UTC). Can be up to 30 days from order initiation. | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- ### Request ```txt RAW 8=FIX.4.4|9=247|35=s|34=2|49=TESTOE001|52=20190905-13:29:45.762|56=GEMINI|40=2|44=10000|55=btcusd|60=20190905-13:29:45.762|548=49382061|549=1|550=0|552=2|54=1|11=63216195|583=R485E04Q|38=25.01|54=2|11=44996792|583=7467JVXP|38=25.01|126=20190905-15:29:45.762|10=236| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 247 35 MsgType: NewOrderCross (s) 34 MsgSeqNum: 2 49 SenderCompID: TESTOE001 52 SendingTime: 20190905-13:29:45.762 56 TargetCompID: GEMINI BODY 40 OrdType: LIMIT (2) 44 Price: 10000 55 Symbol: BTCUSD 60 TransactTime: 20190905-13:29:45.762 548 CrossID: 49382061 549 CrossType: Cross trade which is either completely executed or not executed at all (1) 550 CrossPrioritization: No priority (0) 552 NoSides: Two Sides (2) 126 ExpireTime: 20190905-15:29:45.762 REPEATING GROUPS 54 Side: BUY (1) 11 ClOrdID: 63216195 583 ClOrdLinkID: R485E04Q 38 OrderQty: 25.01 54 Side: SELL (2) 11 ClOrdID: 44996792 583 ClOrdLinkID: 7467JVXP 38 OrderQty: 25.01 TRAILER 10 CheckSum: 236 ``` ### Execution Report After submitting a [new order cross as a broker](/trading/fix/order-entry/gemini-clearing/fix-broker-support), 2 execution reports will be sent (one for each side of the trade). ```txt RAW 8=FIX.4.4|9=211|35=8|34=2|49=GEMINI|52=20190905-13:29:45.928|56=TESTOE001|6=10000|11=63216195|14=25.01|17=1567690185928|37=G9LVQOX5|39=0|44=10000|54=1|55=btcusd|60=20190905-13:29:45.928|150=0|151=25.01|583=R485E04Q|10=239| 8=FIX.4.4|9=211|35=8|34=3|49=GEMINI|52=20190905-13:29:45.929|56=TESTOE001|6=10000|11=44996792|14=25.01|17=1567690185928|37=G9LVQOX5|39=0|44=10000|54=2|55=btcusd|60=20190905-13:29:45.928|150=0|151=25.01|583=7467JVXP|10=054| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 211 35 MsgType: ExecutionReport (8) 34 MsgSeqNum: 2 49 SenderCompID: GEMINI 52 SendingTime: 20190905-13:29:45.928 56 TargetCompID: TESTOE001 BODY 6 AvgPx: 10000 11 ClOrdID: 63216195 14 CumQty: 25.01 17 ExecID: 1567690185928 37 OrderID: G9LVQOX5 39 OrdStatus: NEW (0) 44 Price: 10000 54 Side: BUY (1) 55 Symbol: BTCUSD 60 TransactTime: 20190905-13:29:45.928 150 ExecType: NEW (0) 151 LeavesQty: 25.01 583 ClOrdLinkID: Clearing ID for corresponding Gemini account (R485E04Q) Trailer 10 CheckSum: 239 HEADER 8 BeginString: FIX.4.4 9 BodyLength: 211 35 MsgType: ExecutionReport (8) 34 MsgSeqNum: 3 49 SenderCompID: GEMINI 52 SendingTime: 20190905-13:29:45.929 56 TargetCompID: TESTOE001 BODY 6 AvgPx: 10000 11 ClOrdID: 44996792 14 CumQty: 25.01 17 ExecID: 1567690185928 37 OrderID: G9LVQOX5 39 OrdStatus: NEW (0) 44 Price: 10000 54 Side: SELL (2) 55 Symbol: BTCUSD 60 TransactTime: 20190905-13:29:45.928 150 ExecType: NEW (0) 151 LeavesQty: 25.01 583 ClOrdLinkID: Clearing ID for corresponding Gemini account (7467JVXP) Trailer 10 CheckSum: 054 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/gemini-clearing/clearing-order-confirmation.md # Clearing order confirmation ### New Order Cross Confirmation To confirm a Gemini Clearing order, the counterparty must login and confirm the order. The order must be confirmed with the same details provided in the initial order. Gemini will respond to a [New Order Cross ``](https://www.onixs.biz/fix-dictionary/4.4/msgtype_s_115.html) with an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report). --- ### Fields | Tag | Name | Req | Description | | --- | ------------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `s` | | 11 | [ClOrdId](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) | Y | Unique identifier of the cross order as assigned by the institution. Uniqueness must be guaranteed by the institution for the duration of the connection to the order entry channel. | | 38 | [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html) | Y | Decimal quantity of quantity currency to buy or sell | | 40 | [OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) | Y | Order type.

    Valid values:
    `2 = Limit` | | 44 | [Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html) | Y | Decimal price. Price is denominated in `CCY1`. | | 54 | [Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) | Y | Side of the initiator of the order.

    Valid values:
    `1 = Buy`
    `2 = Sell` | | 55 | [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) | Y | Ticker symbol of the order.

    See [Supported Symbols](/market-data/symbols-and-minimums) for valid values. | | 60 | [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html) | Y | Time of order creation (expressed in UTC). | | 117 | [QuoteID](https://www.onixs.biz/fix-dictionary/4.4/tagnum_117.html) | Y | Also called `clearing_id`, will be provided to initiator in [tag `<37>`](https://www.onixs.biz/fix-dictionary/4.4/tagnum_37.html) of the execution report, or if a `counterparty_id` is used, the `clearing_id` will show up in the counterparty's blotter on the [web interface](https://www.gemini.com/clearing) | | 126 | [ExpireTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_126.html) | Y | Time of order expiration (expressed in UTC). Can be up to 30 days from order initiation. | | 548 | [CrossID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_548.html) | Y | Unique identifier for a cross order | | 549 | [CrossType](https://www.onixs.biz/fix-dictionary/4.4/tagnum_549.html) | Y | Type of cross being submitted.

    Valid values:
    `1 = Cross trade which is either completely executed or not` | | 550 | [CrossPrioritization](https://www.onixs.biz/fix-dictionary/4.4/tagnum_550.html) | Y | Indicates if one side or the other of a cross order should be prioritized.

    Valid values:
    `0 = None` | | 552 | [NoSides](https://www.onixs.biz/fix-dictionary/4.4/tagnum_552.html) | Y | Number of [side `<54>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) repeating group instances

    Valid values:
    `1 = One Side` | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- ### Request ```txt RAW 8=FIX.4.4|9=191|35=s|34=2|49=TESTOE002|52=20190806-18:49:03.689|56=GEMINI|40=2|44=12000|55=btcusd|60=20190806-18:49:03.689|117=4KZ306ZG|548=86478800|549=1|550=0|552=1|54=2|11=46081666|38=2.22222|10=103| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 191 35 MsgType: NewOrderCross (s) 34 MsgSeqNum: 2 49 SenderCompID: TESTOE002 52 SendingTime: 20190806-18:49:03.689 56 TargetCompID: GEMINI BODY 40 OrdType: LIMIT (2) 44 Price: 12000 55 Symbol: BTCUSD 60 TransactTime: 20190806-18:49:03.689 117 QuoteID: 2DZ4MPQM 548 CrossID: 86478800 549 CrossType: 1 550 CrossPrioritization: 0 552 Number of sides: 1 54 Side: SELL (2) 11 ClOrdID: 46081666 38 OrderQty: 2.22222 TRAILER 10 CheckSum: 103 ``` ### Execution Report See [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) for more details on execution report tags. ```txt RAW 8=FIX.4.4|9=205|35=8|34=2|49=GEMINI|52=20190806-18:49:03.839|56=TESTOE002|6=12000|11=46081666|14=2.22222|17=1565117343839|37=4KZ306ZG|39=0|44=12000|54=2|55=BTCUSD|58=ORDER_CONFIRMED|60=20190806-18:49:03.839|150=0|151=2.22222|10=057| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 205 35 MsgType: ExecutionReport (8) 34 MsgSeqNum: 2 49 SenderCompID: GEMINI 52 SendingTime: 20190806-18:49:03.839 56 TargetCompID: TESTOE002 BODY 6 AvgPx: 12000 11 ClOrdID: 46081666 14 CumQty: 2.22222 17 ExecID: 1565117343839 37 OrderID: 4KZ306ZG 39 OrdStatus: New (0) 44 Price: 12000 54 Side: SELL (2) 55 Symbol: BTCUSD 58 Text: ORDER_CONFIRMED 60 TransactTime: 20190806-18:49:03.839 150 ExecType: NEW (0) 151 LeavesQty: 2.22222 TRAILER 10 CheckSum: 057 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/gemini-clearing/clearing-order-cancellation.md # Clearing order cancellation ### Cross Order Cancel Request Once an initiator places a [new order cross](/trading/fix/order-entry/gemini-clearing/initiate-a-clearing-order), a [cross order cancellation request](https://www.onixs.biz/fix-dictionary/4.4/msgType_u_117.html) can be placed to cancel the order before it is confirmed by the counterparty. Once the order is confirmed by the counterparty, it cannot be cancelled. --- ### Fields | Tag | Name | Req | Description | | --- | ------------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `u` | | 37 | [OrderID](https://www.onixs.biz/fix-dictionary/4.4/tagnum_37.html) | Y | The OrderID given in [tag `<37>`](https://www.onixs.biz/fix-dictionary/4.4/tagnum_37.html) in the [Execution Report `<8>`](/trading/fix/order-entry/gemini-clearing/initiate-a-clearing-order) of the original order. | | 55 | [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) | Y | Ticker symbol of the order.

    See [Supported Symbols](/market-data/symbols-and-minimums) for valid values. | | 60 | [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html) | Y | Time of order creation (expressed in UTC). | | 548 | [CrossID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_548.html) | Y | Unique identifier for a cross order | | 549 | [CrossType](https://www.onixs.biz/fix-dictionary/4.4/tagnum_549.html) | Y | Type of cross being submitted.

    Valid values:
    `1 = Cross trade which is either completely executed or not` | | 550 | [CrossPrioritization](https://www.onixs.biz/fix-dictionary/4.4/tagnum_550.html) | Y | Indicates if one side or the other of a cross order should be prioritized.

    Valid values:
    `0 = None` | | 551 | [OrigCrossID](https://www.onixs.biz/fix-dictionary/4.4/tagnum_551.html) | Y | CrossID given in the original [new order cross](/trading/fix/order-entry/gemini-clearing/initiate-a-clearing-order) in [tag 548](https://www.onixs.biz/fix-dictionary/4.4/tagNum_548.html) | | 552 | [NoSides](https://www.onixs.biz/fix-dictionary/4.4/tagnum_552.html) | Y | Number of [side `<54>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) repeating group instances

    Valid values:
    `1 = One Side` | | 54 | [Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) | Y | Side of the initiator of the order.

    Valid values:
    `1 = Buy`
    `2 = Sell` | | 41 | [OrigClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagnum_41.html) | Y | ClOrdID given in the original [new order cross](/trading/fix/order-entry/gemini-clearing/initiate-a-clearing-order). | | 11 | [ClOrdId](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) | Y | Unique identifier of the cross order as assigned by the institution. Uniqueness must be guaranteed by the institution for the duration of the connection to the order entry channel. | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- ### Request ```txt RAW 8=FIX.4.4|9=201|34=3|35=u|49=TESTOE001|52=20190806-20:30:28.898|56=GEMINI|37=2DZ4MPQM|55=BTCUSD|60=20190806-20:30:28.898|548=73180000|549=1|550=0|551=26990504|552=1|54=2|41=87749738|11=76494933|38=2.22222|10=128| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 201 34 MsgSeqNum: 3 35 MsgType: Cross Order Cancel Request 49 SenderCompID: TESTOE001 52 SendingTime: 20190806-20:30:28.898 56 TargetCompID: GEMINI BODY 37 OrderID: 2DZ4MPQM 55 Symbol: BTCUSD 60 TransactTime: 20190806-20:30:28.898 548 CrossID: 73180000 549 CrossType: 1 550 CrossPrioritization: 0 551 OrigCrossID: 26990504 552 NoSides: 1 54 Side: SELL (2) 41 OrigClOrdID: 87749738 11 ClOrdID: 76494933 38 OrderQty: 2.22222 TRAILER 10 CheckSum: 066 ``` ### Execution Report ```txt RAW 8=FIX.4.4|9=202|35=8|34=3|49=GEMINI|52=20190806-20:30:28.947|56=TESTOE001|6=0|11=76494933|14=2.22222|17=1565123428947|37=2DZ4MPQM|39=4|54=2|55=BTCUSD|58=ORDER_CANCELED|60=20190806-20:30:28.947|150=4|151=2.22222|10=127| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 202 35 MsgType: ExecutionReport (8) 34 MsgSeqNum: 3 49 SenderCompId: GEMINI 52 SendingTime: 20190806-20:30:28.947 56 TargetCompID: TESTOE001 BODY 6 AvgPx: 0 11 ClOrdID: 76494933 14 CumQty: 2.22222 17 ExecID: 1565123428947 37 OrderID: 2DZ4MPQM 39 OrdStatus: CANCELED (4) 54 Side: SELL (2) 55 Symbol: BTCUSD 58 Text: ORDER_CANCELLED 60 TransactTime: 20190806-20:30:28.947 150 ExecType: CANCELED (4) 151 LeavesQty: 2.22222 TRAILER 10 CheckSum: 127 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/exchange-bound-messages/order-cancel-request.md # Order Cancel Request # The [Order Cancel Request ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_F_70.html) message requests the cancellation of all of the remaining quantity of an existing order. Gemini cancels order on the basis of the value in the [OrigClOrdID `<41>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_41.html) field. All other fields are required in the FIX specs but will be disregarded. - if the order can successfully be canceled before being completely filled, an [Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) with [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) set to `4 = Canceled` - if the request fails, an [Order Cancel Reject `<9>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_9_9.html) message with [CxlRejReason `<102>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_102.html) of `1 = Unknown order` --- ### Fields `](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) of the order to cancel. Needs to correspond to a current outstanding order submitted during this trading session.", ], [ "54", "[Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html)", "Y", "Side of the order to cancel.\n\n \u00A0 \n\nValid values:\n\n`1 = Buy`\n\n`2 = Sell`", ], [ "55", "[Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html)", "Y", "Ticker symbol of the order.\n\n \u00A0 \n\nSee [Supported Symbols](/market-data/symbols-and-minimums) for valid values.", ], [ "60", "[TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html)", "Y", "Time that the order cancel request was initiated by the institution (expressed in UTC).", ], ["", "[Standard Trailer](/trading/fix/overview/standard-trailer)", "Y", ""], ]} /> --- URL: https://developer.gemini.com/trading/fix/order-entry/exchange-bound-messages/new-order-single-stop-limit.md # New Order Single (STOP LIMIT) # To submit a new stop limit order to Gemini, send a [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) message with [OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) Stop Limit. Gemini will respond to a [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) message with an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report). See [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) for examples. If Gemini receives a [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) message with the [PossResend `<97>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_97.html) flag set to `Y` in the header, that order will be rejected with [Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html). --- ### Fields `](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) is `4 = Stop Limit`.", ], [ "99", "[StopPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_99.html)", "Y", "Decimal price.\n\n \u00A0 \n\n`StopPx` is required when [OrdType `<40>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) is `4 = Stop Limit`.\n\n \u00A0 \n\nWhen [Side `<54>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) is `1 = Buy`, `StopPx` needs to be less than or equal to `Price`. \n\nWhen [Side `<54>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) is `2 = Sell`, `StopPx` needs to be greater than or equal to `Price`.", ], [ "54", "[Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html)", "Y", "Side of the order.\n\n \u00A0 \n\nValid values:\n\n`1 = Buy`\n\n`2 = Sell`", ], [ "55", "[Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html)", "Y", "Ticker symbol of the order.\n\n \u00A0 \n\nSee [Supported Symbols](/market-data/symbols-and-minimums) for valid values.", ], [ "59", "[TimeInForce](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html)", "Y", "Specifies how long the order remains in effect.\n\n \u00A0 \n\nValid value:\n`1 = Good Till Cancel (GTC)`", ], [ "60", "[TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html)", "Y", "Time of order creation (expressed in UTC).", ], [ "544", "[CashMargin](https://www.onixs.biz/fix-dictionary/4.4/tagNum_544.html)", "N", "**Not supported for stop-limit orders.**\n\n \u00A0 \n\nSpecifying this field will result in a reject with error message: \"CashMargin <544> not supported for stop-limit orders\".\n\n \u00A0 \n\nStop-limit orders are always processed as non-margin (cash) orders.", ], ["", "[Standard Trailer](/trading/fix/overview/standard-trailer)", "Y", ""], ]} /> --- URL: https://developer.gemini.com/trading/fix/order-entry/exchange-bound-messages/new-order-single-market.md # New Order Single (MARKET) # To submit a new market order to Gemini, send a [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) message with OrdType Market. Gemini will respond to a [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) message with an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report). See [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) for examples. --- ### Fields not supported for sell orders\".", ], ["", "[Standard Trailer](/trading/fix/overview/standard-trailer)", "Y", ""], ]} /> --- URL: https://developer.gemini.com/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit.md # New Order Single (LIMIT) # To submit a new limit order to Gemini, send a [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) message with [OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) Limit. Gemini will respond to a [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) message with an [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report). See [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) for examples. If Gemini receives a [New Order Single ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_D_68.html) message with the [PossResend `<97>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_97.html) flag set to `Y` in the header, that order will be rejected with [Business Message Reject ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_j_106.html). --- ### Fields `](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html).\n\n \u00A0 \n\n`6 = Participate don't initiate` (denotes maker-or-cancel when combined with TimeInForce value `1 = Good Till Cancel (GTC)`)", ], [ "38", "[OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html)", "Y", "Decimal quantity. Full quantity will be visible on the book.", ], [ "110", "[MinQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_110.html)", "N\\*", "", ], [ "40", "[OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html)", "Y", "Order type.\n\n \u00A0 \n\nValid values:\n\n`2 = Limit`", ], [ "44", "[Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html)", "Y", "Decimal price.\n\n \u00A0 \n\nPrice is required when [OrdType `<40>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) is `2 = Limit`.", ], [ "54", "[Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html)", "Y", "Side of the order.\n\n \u00A0 \n\nValid values:\n\n`1 = Buy`\n\n`2 = Sell`", ], [ "55", "[Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html)", "Y", "Ticker symbol of the order.\n\n \u00A0 \n\nSee [Supported Symbols](/market-data/symbols-and-minimums) for valid values.", ], [ "59", "[TimeInForce](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html)", "Y", "Specifies how long the order remains in effect.\n\n \u00A0 \n\nValid values:\n\n`1 = Good Till Cancel (GTC)`\n\n`3 = Immediate Or Cancel (IOC)`\n\n`4 = Fill Or Kill (FOK)`", ], [ "60", "[TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html)", "Y", "Time of order creation (expressed in UTC).", ], [ "544", "[CashMargin](https://www.onixs.biz/fix-dictionary/4.4/tagNum_544.html)", "N", "Identifies whether order is a margin order or a non-margin order.\n\n \u00A0 \n\nValid values:\n\n`1 = Cash`\n\n`2 = Margin Open`\n\n \u00A0 \n\nThis field is optional. If not provided, the order is assumed to be a non-margin (cash) order.\n\nOnly accounts enabled for spot margin trading can specify margin orders.\n\n`3 = Margin Close` is not supported.\n\n**Restriction**: This field is only supported for buy orders (Side=1). Specifying this field on sell orders (Side=2) will result in a reject with error message: \"CashMargin <544> not supported for sell orders\".", ], ["", "[Standard Trailer](/trading/fix/overview/standard-trailer)", "Y", ""], ]} /> --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/server-side-cancellations.md # Server Side Cancellations ### Maker-Or-Cancel This is an example of an Execution Report for an order that was canceled because part or all of the `maker-or-cancel` order would fill immediately: ```txt 8=FIX.4.4|9=207|35=8|34=6|49=GEMINI|52=20180530-15:25:13.110|56=DEV|6=0|11=GHDzdNUUXaMMDZdfwe|14=0|17=48|18=6|37=46|38=10|39=4|44=448.01|54=1|55=BTCUSD|58=MAKER_OR_CANCEL_WOULD_TAKE|59=1|60=20150218-18:45:02.030|150=4|151=0|10=031| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 207 34 MsgSeqNum: 6 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180530-15:25:13.110 56 TargetCompID: DEV BODY 6 AvgPx: 0 11 ClOrdID: GHDzdNUUXaMMDZdfwe 14 CumQty: 0 17 ExecID: 48 18 ExecInst: PARTICIPATE_DONT_INITIATE (6) 37 OrderID: 46 38 OrderQty: 10 39 OrdStatus: CANCELED (4) 44 Price: 448.01 54 Side: BUY (1) 55 Symbol: BTCUSD 58 Text: MAKER_OR_CANCEL_WOULD_TAKE 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20150218-18:45:02.030 150 ExecType: CANCELED (4) 151 LeavesQty: 0 TRAILER 10 CheckSum: 031 ``` ### Immediate-Or-Cancel This is an example of an Execution Report for an order that was canceled because the immediate-or-cancel order would not fill immediately: ```txt 8=FIX.4.4|9=212|35=8|34=4|49=GEMINI|52=20180530-15:34:56.648|56=DEV|6=448.06|11=GHDzdNUUXaMMDZdfwe|14=1.2|17=35|37=31|38=5.0|39=4|44=448.06|54=2|55=BTCUSD|58=IMMEDIATE_OR_CANCEL_WOULD_POST|59=3|60=20150218-18:45:02.017|150=4|151=0|10=062| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 212 34 MsgSeqNum: 4 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180530-15:34:56.648 56 TargetCompID: DEV BODY 6 AvgPx: 448.06 11 ClOrdID: GHDzdNUUXaMMDZdfwe 14 CumQty: 1.2 17 ExecID: 35 37 OrderID: 31 38 OrderQty: 5.0 39 OrdStatus: CANCELED (4) 44 Price: 448.06 54 Side: SELL (2) 55 Symbol: BTCUSD 58 Text: IMMEDIATE_OR_CANCEL_WOULD_POST 59 TimeInForce: IMMEDIATE_OR_CANCEL (3) 60 TransactTime: 20150218-18:45:02.017 150 ExecType: CANCELED (4) 151 LeavesQty: 0 TRAILER 10 CheckSum: 062 ``` View the most common reasons [here](/trading/fix/order-entry/client-bound-messages/order-cancel-reasons). --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/order-cancel-request.md # Order Cancel Request ### Request This is an Order Cancel Request that cancels an order of 1 BTC that was previously entered with an [CLOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) of `z35u64KR1gen7f2SpB`: ```txt RAW 8=FIX.4.4|9=147|35=F|34=3|49=TRADEBOTOE002|52=20180425-17:57:59.000|56=GEMINI|11=GHDzdNUUXaMMDZdfwe|38=1|41=z35u64KR1gen7f2SpB|54=2|55=BTCUSD|60=20180425-17:57:59|10=185| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 147 34 MsgSeqNum: 3 35 MsgType: OrderCancelRequest (F) 49 SenderCompID: TRADEBOTOE002 52 SendingTime: 20180425-17:57:59.000 56 TargetCompID: GEMINI BODY 11 ClOrdID: GHDzdNUUXaMMDZdfwe 38 OrderQty: 1 41 OrigClOrdID: z35u64KR1gen7f2SpB 54 Side: SELL (2) 55 Symbol: BTCUSD 60 TransactTime: 20180425-17:57:59 TRAILER 10 CheckSum: 185 ``` ### Response and its associated Execution Report response: ```txt RAW 8=FIX.4.4|9=220|35=8|34=3|49=GEMINI|52=20180425-17:57:59.538|56=TRADEBOTOE002|6=0|11=GHDzdNUUXaMMDZdfwe|14=0|17=335278132|37=335278128|38=1|39=4|41=z35u64KR1gen7f2SpB|44=93392.64|54=2|55=BTCUSD|58=REQUESTED|59=1|60=20180425-17:57:59.537|150=4|151=0|10=254| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 220 34 MsgSeqNum: 3 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:57:59.538 56 TargetCompID: TRADEBOTOE002 BODY 6 AvgPx: 0 11 ClOrdID: GHDzdNUUXaMMDZdfwe 14 CumQty: 0 17 ExecID: 335278132 37 OrderID: 335278128 38 OrderQty: 1 39 OrdStatus: CANCELED (4) 41 OrigClOrdID: z35u64KR1gen7f2SpB 44 Price: 93392.64 54 Side: SELL (2) 55 Symbol: BTCUSD 58 Text: REQUESTED 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20180425-17:57:59.537 150 ExecType: CANCELED (4) 151 LeavesQty: 0 TRAILER 10 CheckSum: 254 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/new-order-single.md # New Order Single ### Request This is a New Order Single (`D` in [MsgType `<35>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_35.html)) request: ```txt RAW 8=FIX.4.4|9=144|35=D|34=2|49=TRADEBOTOE002|52=20180425-17:56:41.000|56=GEMINI|11=iWM60sx3dreT9N9yEE|38=1|40=2|44=10000|54=1|55=BTCUSD|59=1|60=20180425-17:56:41|10=073| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 144 34 MsgSeqNum: 2 35 MsgType: NewOrderSingle (D) 49 SenderCompID: TRADEBOTOE002 52 SendingTime: 20180425-17:56:41.000 56 TargetCompID: GEMINI BODY 11 ClOrdID: iWM60sx3dreT9N9yEE 38 OrderQty: 1 40 OrdType: LIMIT (2) 44 Price: 10000 54 Side: BUY (1) 55 Symbol: BTCUSD 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20180425-17:56:41 TRAILER 10 CheckSum: 073 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/new-order-single-on-behalf-of-third-party.md # New Order Single on Behalf of Third Party ### Request This is a New Order Single request on behalf of a [third party](/trading/fix/order-entry/third-party-support): ```txt RAW 8=FIX.4.4|9=162|35=D|34=2|49=TRADEBOTOE001|52=20180425-17:58:11.000|56=GEMINI|115=AA1AA1AAA1|11=FLWC3iFc6ygIxFSKVY|38=10|40=2|44=8490.44|54=2|55=BTCUSD|59=1|60=20180425-17:58:11|10=024| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 162 34 MsgSeqNum: 2 35 MsgType: NewOrderSingle (D) 49 SenderCompID: TRADEBOTOE001 52 SendingTime: 20180425-17:58:11.000 56 TargetCompID: GEMINI 115 OnBehalfOfCompID: AA1AA1AAA1 BODY 11 ClOrdID: FLWC3iFc6ygIxFSKVY 38 OrderQty: 10 40 OrdType: LIMIT (2) 44 Price: 8490.44 54 Side: SELL (2) 55 Symbol: BTCUSD 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20180425-17:58:11 TRAILER 10 CheckSum: 024 ``` ### Execution Report and its associated Execution Report: ```txt RAW 8=FIX.4.4|9=214|35=8|34=2|49=GEMINI|52=20180425-17:58:12.286|56=TRADEBOTOE001|115=AA1AA1AAA1|6=0|11=FLWC3iFc6ygIxFSKVY|14=0|17=335278137|37=335278136|38=10|39=0|44=8490.44|54=2|55=BTCUSD|59=1|60=20180425-17:58:12.285|150=0|151=10|10=155| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 214 34 MsgSeqNum: 2 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:58:12.286 56 TargetCompID: TRADEBOTOE001 128 DeliverToCompID: AA1AA1AAA1 BODY 6 AvgPx: 0 11 ClOrdID: FLWC3iFc6ygIxFSKVY 14 CumQty: 0 17 ExecID: 335278137 37 OrderID: 335278136 38 OrderQty: 10 39 OrdStatus: NEW (0) 44 Price: 8490.44 54 Side: SELL (2) 55 Symbol: BTCUSD 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20180425-17:58:12.285 150 ExecType: NEW (0) 151 LeavesQty: 10 TRAILER 10 CheckSum: 155 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/new-market-sell-order.md # New Market SELL Order ### Request This is an example of a request for a market SELL order: ```txt RAW 8=FIX.4.4|9=126|35=D|34=2|49=DEV|52=20181023-17:49:51.691|56=GEMINI|11=YLWC3xFi6ygIxFSKVY|40=1|54=1|55=BTCUSD|60=20150218-18:45:02.003|152=500.0|10=112| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 126 34 MsgSeqNum: 2 35 MsgType: NewOrderSingle (D) 49 SenderCompID: DEV 52 SendingTime: 20181023-17:49:51.691 56 TargetCompID: GEMINI BODY 11 ClOrdID: YLWC3xFi6ygIxFSKVY 38 OrderQty: 2.0 40 OrdType: MARKET (1) 54 Side: SELL (2) 55 Symbol: BTCUSD 60 TransactTime: 20150218-18:45:02.010 TRAILER 10 CheckSum: 112 ``` :::info **Notes**: - [OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html): `MARKET (1)` indicates this is a Market order - [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html): `2.0` The amount of BTC to sell for USD ::: ### Execution Report and the associated Execution Report for a market SELL order: ```txt RAW 8=FIX.4.4|9=161|35=8|34=2|49=GEMINI|52=20181023-20:26:27.359|56=DEV|6=0|11=YLWC3xFi6ygIxFSKVY|14=0|17=43|37=42|38=2.0|39=0|54=2|55=BTCUSD|60=20150218-18:45:02.042|150=0|151=2.0|10=113| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 161 34 MsgSeqNum: 2 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20181023-20:26:27.359 56 TargetCompID: DEV BODY 6 AvgPx: 0 11 ClOrdID: YLWC3xFi6ygIxFSKVY 14 CumQty: 0 17 ExecID: 43 37 OrderID: 42 38 OrderQty: 2.0 39 OrdStatus: NEW (0) 54 Side: SELL (2) 55 Symbol: BTCUSD 60 TransactTime: 20150218-18:45:02.042 150 ExecType: NEW (0) 151 LeavesQty: 2.0 TRAILER 10 CheckSum: 113 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/new-market-buy-order.md # New Market BUY Order ### Request This is an example of a request for a market BUY order: ```txt RAW 8=FIX.4.4|9=126|35=D|34=2|49=DEV|52=20181023-17:49:51.691|56=GEMINI|11=FLWC3iFi6ygIxFSKVY|40=1|54=1|55=BTCUSD|60=20150218-18:45:02.003|152=500.0|10=235| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 126 34 MsgSeqNum: 2 35 MsgType: NewOrderSingle (D) 49 SenderCompID: DEV 52 SendingTime: 20181023-17:49:51.691 56 TargetCompID: GEMINI BODY 11 ClOrdID: FLWC3iFi6ygIxFSKVY 40 OrdType: MARKET (1) 54 Side: BUY (1) 55 Symbol: BTCUSD 60 TransactTime: 20150218-18:45:02.003 152 CashOrderQty: 500.0 TRAILER 10 CheckSum: 235 ``` :::info **Notes**: - [OrdType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html): `MARKET (1)` indicates this is a Market order - [CashOrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagnum_152.html): `500.0` The amount of USD used to buy BTC. CashOrderQty is always the amount in price currency used to buy the quantity currency ::: ### Execution Report and the associated Execution Report for a market BUY order: ```txt RAW 8=FIX.4.4|9=163|35=8|34=2|49=GEMINI|52=20181023-17:49:51.943|56=DEV|6=0|11=FLWC3iFi6ygIxFSKVY|14=0|17=43|37=42|39=0|54=1|55=BTCUSD|60=20150218-18:45:02.042|150=0|151=500.0|152=500.0|10=069| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 163 34 MsgSeqNum: 2 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20181023-17:49:51.943 56 TargetCompID: DEV BODY 6 AvgPx: 0 11 ClOrdID: FLWC3iFi6ygIxFSKVY 14 CumQty: 0 17 ExecID: 43 37 OrderID: 42 39 OrdStatus: NEW (0) 54 Side: BUY (1) 55 Symbol: BTCUSD 60 TransactTime: 20150218-18:45:02.042 150 ExecType: NEW (0) 151 LeavesQty: 500.0 152 CashOrderQty: 500.0 TRAILER 10 CheckSum: 069 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/market-order-partial-fill.md # Market Order Partial Fill In the rare case a market order sweeps the book and only partially fills, two Execution Reports will be generated: one describing the partial fill and another cancelling the market order as it was only partially filled. ### Partial Fill Execution Report Here is an example of the partial fill Execution Report: ```txt RAW 8=FIX.4.4|9=237|35=8|34=3|49=GEMINI|52=20181023-18:05:07.978|56=DEV|6=448.06|11=CLWC3iFi6ygIyFSKVY|12=8.9612|13=3|14=1.0|17=44|31=448.06|32=1.0|37=42|39=1|54=1|55=BTCUSD|60=20150218-18:45:02.043|150=F|151=42.9788|152=500.0|479=USD|851=2|10=172| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 237 34 MsgSeqNum: 3 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20181023-18:05:07.978 56 TargetCompID: DEV BODY 6 AvgPx: 448.06 11 ClOrdID: CLWC3iFi6ygIyFSKVY 12 Commission: 8.9612 13 CommType: ABSOLUTE (3) 14 CumQty: 1.0 17 ExecID: 44 31 LastPx: 448.06 32 LastQty: 1.0 37 OrderID: 42 39 OrdStatus: PARTIALLY_FILLED (1) 54 Side: BUY (1) 55 Symbol: BTCUSD 60 TransactTime: 20150218-18:45:02.043 150 ExecType: TRADE (F) 151 LeavesQty: 42.9788 152 CashOrderQty: 500.0 479 CommCurrency: USD 851 LastLiquidityInd: REMOVED_LIQUIDITY (2) TRAILER 10 CheckSum: 172 ``` :::info **Notes**: [CumQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html): `1.0` is the total quantity filled. [LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html): `42.978800000` indicates the unfilled notional value of the order. [CashOrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagnum_152.html): `500.0` is the original notional value desired to be filled. ::: ### Order Cancellation Execution Report and the following Execution Report representing a cancellation of the order: ```txt RAW 8=FIX.4.4|9=199|35=8|34=4|49=GEMINI|52=20181023-18:05:07.984|56=DEV|6=448.06|11=CLWC3iFi6ygIyFSKVY|14=1.0|17=46|37=42|39=4|54=1|55=BTCUSD|58=MARKET_ORDER_SWEPT_BOOK|60=20150218-18:45:02.045|150=4|151=0|152=500.0|10=TRAILER| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 199 34 MsgSeqNum: 4 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20181023-18:05:07.984 56 TargetCompID: DEV BODY 6 AvgPx: 448.06 11 ClOrdID: CLWC3iFi6ygIyFSKVY 14 CumQty: 1.0 17 ExecID: 46 37 OrderID: 42 39 OrdStatus: CANCELED (4) 54 Side: BUY (1) 55 Symbol: BTCUSD 58 Text: MARKET_ORDER_SWEPT_BOOK 60 TransactTime: 20150218-18:45:02.045 150 ExecType: CANCELED (4) 151 LeavesQty: 0 152 CashOrderQty: 500.0 TRAILER 10 CheckSum: 184 ``` :::info **Notes**: [TEXT](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html): `MARKET_ORDER_SWEPT_BOOK` text indicating the cancel reason for the order ::: --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/market-order-fill.md # Market Order Fill ### Execution Report This is an example of an Execution Report for a filled market BUY order: ```txt RAW 8=FIX.4.4|9=297|35=8|34=3|49=GEMINI|52=20181023-17:49:51.958|56=DEV|6=448.06|11=FLWC3iFi6ygIxFSKVY|12=9.80392156863|13=3|14=1.0940411517|17=44|31=448.06|32=1.0940411517|37=42|39=2|54=1|55=BTCUSD|60=20150218-18:45:02.043|150=F|151=0|152=500.0|479=USD|851=2|10=089| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 297 34 MsgSeqNum: 3 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20181023-17:49:51.958 56 TargetCompID: DEV BODY 6 AvgPx: 448.06 11 ClOrdID: FLWC3iFi6ygIxFSKVY 12 Commission: 9.8039215686 13 CommType: ABSOLUTE (3) 14 CumQty: 1.0940411517 17 ExecID: 44 31 LastPx: 448.06 32 LastQty: 1.0940411517 37 OrderID: 42 39 OrdStatus: FILLED (2) 54 Side: BUY (1) 55 Symbol: BTCUSD 60 TransactTime: 20150218-18:45:02.043 150 ExecType: TRADE (F) 151 LeavesQty: 0 152 CashOrderQty: 500.0 479 CommCurrency: USD 851 LastLiquidityInd: REMOVED_LIQUIDITY (2) TRAILER 10 CheckSum: 089 ``` --- URL: https://developer.gemini.com/trading/fix/order-entry/examples/execution-reports.md # Execution Reports The following section contains examples of execution reports. :::info **Notes**: - Explanatory notes about the execution report fields are provided after each example. Additional information about the fields can be found in the [Execution Report `<8>`](/trading/fix/order-entry/client-bound-messages/execution-report) documentation and [here](https://www.onixs.biz/fix-dictionary/4.4/fields_by_tag.html). - Gemini associates the following [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) outcomes with each supported [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html): ::: | [ExecType`<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) | [OrdStatus`<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) | | --------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | `0 = New` | `0 = New`
    `1 = Partially filled`
    `2 = Filled`
    `8 = Rejected` | | `F = Trade` | `1 = Partially filled`
    `2 = Filled` | | `4 = Canceled` | `4 = Canceled` | | `8 = Rejected` | `8 = Rejected` | --- ## Execution Reports ### New Order This is an example of an [Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.2/msgType_8_8.html) in response to `TRADEBOTOE002`'s [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit): ```txt RAW 8=FIX.4.4|9=195|35=8|34=2|49=GEMINI|52=20180425-17:56:42.071|56=TRADEBOTOE002|6=0|11=iWM60sx3dreT9N9yEE|14=0|17=335278099|37=335278098|38=1|39=0|44=10000|54=1|55=BTCUSD|59=1|60=20180425-17:56:42.071|150=0|151=1|10=163| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 195 34 MsgSeqNum: 2 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:56:42.071 56 TargetCompID: TRADEBOTOE002 BODY 6 AvgPx: 0 11 ClOrdID: iWM60sx3dreT9N9yEE 14 CumQty: 0 17 ExecID: 335278099 37 OrderID: 335278098 38 OrderQty: 1 39 OrdStatus: NEW (0) 44 Price: 10000 54 Side: BUY (1) 55 Symbol: BTCUSD 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20180425-17:56:42.071 150 ExecType: NEW (0) 151 LeavesQty: 1 TRAILER 10 CheckSum: 163 ``` :::info **Notes**: - [AvgPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_6.html): `0` is the avg price of all fills on the order. Given that this is a new order execution report, there are no fills to average. - [ClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html): `iWM60sx3dreT9N9yEE` is a unique request identifier assigned by `TRADEBOTOE002` in the [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) field in the original [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit). - [CumQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html): `0` is the total quantity filled. Again, given that this is a new order execution report, there are no fills. - [ExecID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_17.html): `335278099` is an order acceptance event id assigned by Gemini; see [Identifiers](/fix-overview/allowed-characters) - [ExecType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html): `NEW (0)` indicates that this execution report is for a new order. - [LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html): `1` represents the quantity left open for execution. In a new order, this should equal [OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html). - [OrderID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_37.html): `335278098` is a globally unique order event id assigned by Gemini at the time the order is submitted. - [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html): `1` represents the number of BTC ordered. - [OrdStatus](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html): `NEW (0)` indicates that the order's status is new. - [Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html): `10000` is the price denominated in USD (the price currency). See [Understanding price and quantity](/trading/fix/order-entry/price-&quantity/understanding-price-&-quantity) for further explanation of price currency vs quantity currency. - [StopPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_99.html): `Not shown in this example. Same format as Price` Required for a [OrdType `<40>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) is `4 = Stop Limit` order entry ACK. - [Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html): `BUY (1)` refers to the side of the order. - [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html): `BTCUSD` is the symbol name. See [Supported Symbols](/market-data/symbols-and-minimums) for a list of valid symbols and an explanation of which currencies price and quantities fields are denominated in. - [TimeInForce](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html): `GOOD_TILL_CANCEL (1)` specifies that the new order is GTC. - [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html): `20180425-17:56:42.071` is the time when the order was created. ::: --- ### Stop Trigger This is an example of an [Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.2/msgType_8_8.html) for a stop trigger message. This will only show up for a [OrdType `<40>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) is `4 = Stop Limit` order once a trigger event occurs, and doubles as an order ack for the newly created child limit order. ```txt RAW 8=FIX.4.4|9=190|35=8|34=5|49=GEMINI|52=20191105-21:16:55.970|56=TRADEBOTOE003|6=0|11=qCBPcHUu8w1|14=0|17=366449739|37=366449738|38=3|39=0|44=6409.65|54=1|55=BTCUSD|59=1|60=20191105-21:16:55.930|150=0|151=3|10=223| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 190 34 MsgSeqNum: 5 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20191105-21:16:55.970 56 TargetCompID: TRADEBOTOE003 BODY 6 AvgPx: 0 11 ClOrdID: qCBPcHUu8w1 14 CumQty: 0 17 ExecID: 366449739 37 OrderID: 366449738 38 OrderQty: 3 39 OrdStatus: NEW (0) 44 Price: 6409.65 54 Side: BUY (1) 55 Symbol: BTCUSD 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20191105-21:16:55.930 150 ExecType: NEW (0) 151 LeavesQty: 3 TRAILER 10 CheckSum: 223 ``` :::info **Notes**: - [AvgPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_6.html): `0` is the avg price of all fills on the order. Given that this is a new order execution report, there are no fills to average. - [ClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html): `qCBPcHUu8w1` is a unique request identifier assigned by `TRADEBOTOE003. - [CumQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html): `0` is the total quantity filled. Again, given that this is a new order execution report for the child limit order, there are no fills. - [ExecID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_17.html): `366449739` is an order acceptance event id assigned by Gemini; see [Identifiers](/fix-overview/allowed-characters) - [ExecType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html): `NEW (0)` indicates that this execution report is for a new order. - [LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html): `3` represents the quantity left open for execution. In a new order, this should equal [OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html). - [OrderID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_37.html): `366449738` is a globally unique order event id assigned by Gemini at the time the stop order triggers. - [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html): `3` represents the number of BTC ordered. - [OrdStatus](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html): `NEW (0)` indicates that the order's status is new. - [Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html): `6409.65` is the price denominated in USD (the price currency). See [Understanding price and quantity](/trading/fix/order-entry/price-&quantity/understanding-price-&-quantity) for further explanation of price currency vs quantity currency. - [Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html): `BUY (1)` refers to the side of the order. - [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html): `BTCUSD` is the symbol name. See [Supported Symbols](/market-data/symbols-and-minimums) for a list of valid symbols and an explanation of which currencies price and quantities fields are denominated in. - [TimeInForce](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html): `GOOD_TILL_CANCEL (1)` specifies that the new order is GTC. - [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html): `20191105-21:16:55.930` is the time when the order was created. ::: --- ### Fill In this scenario, `TRADEBOTOE002`'s order fills at 8400.00. In this case, `TRADEBOTOE002` is on the **taker** side and pays the base fee of 100bps (1.00%). This is an example of an associated execution report: ```txt RAW 8=FIX.4.4|9=248|35=8|34=3|49=GEMINI|52=20180516-22:03:10.031|56=TRADEBOTOE002|6=8400.00|11=af9hLHqlLYAYb3ErKJ|12=8.400000|13=3|14=1|17=336157291|31=8400.00|32=1|37=336157289|38=1|39=2|44=10000|54=1|55=BTCUSD|59=1|60=20180516-22:03:10.030|150=F|151=0|479=USD|851=2|10=116| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 248 34 MsgSeqNum: 3 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180516-22:03:10.031 56 TargetCompID: TRADEBOTOE002 BODY 6 AvgPx: 8400.00 11 ClOrdID: af9hLHqlLYAYb3ErKJ 12 Commission: 8.400000 13 CommType: ABSOLUTE (3) 14 CumQty: 1 17 ExecID: 336157291 31 LastPx: 8400.00 32 LastQty: 1 37 OrderID: 336157289 38 OrderQty: 1 39 OrdStatus: FILLED (2) 44 Price: 10000 54 Side: BUY (1) 55 Symbol: BTCUSD 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20180516-22:03:10.030 150 ExecType: TRADE (F) 151 LeavesQty: 0 479 CommCurrency: USD 851 LastLiquidityInd: REMOVED_LIQUIDITY (2) TRAILER 10 CheckSum: 116 ``` :::info **Notes**: - [AvgPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_6.html): `8400.00` is the avg price of all fills on the order. - [CommType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_13.html): `ABSOLUTE (3)` indicates that [Commision `<12>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_12.html) is a total monetary amount. - [Commission](https://www.onixs.biz/fix-dictionary/4.4/tagNum_12.html): `8.400000` is in USD as indicated by [CommCurrency `<479>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_479.html). - [CumQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html): `1` is the total quantity filled. - [LastLiquidityInd](https://www.onixs.biz/fix-dictionary/4.4/tagNum_851.html): `REMOVED_LIQUIDITY (2)` indicates that `TRADEBOTOE002` was a liquidity taker. - [LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html): `0` represents the quantity left open for execution. In a filled trade, this should equal [OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html) - [CumQty `<14>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html). - [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html): 1 represents the number of BTC ordered. ::: --- ### Partial Fill In this example, `TRADEBOTOE002` is on the **maker** side and receives an execution report for an order with an original quantity of 20 BTC that was partially filled for 10 BTC with 10 BTC remaining for a fee of 0.00%: ```txt RAW 8=FIX.4.4|9=254|35=8|34=5|49=GEMINI|52=20180517-15:07:16.894|56=TRADEBOTOE002|6=8338.67|11=1tfX3IJi9HP87dkqlo|12=0.000000|13=3|14=10|17=336933409|31=8338.67|32=10|37=336933405|38=20|39=1|44=8338.67|54=1|55=BTCUSD|59=3|60=20180517-15:07:16.892|150=F|151=10|479=USD|851=1|10=001| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 254 34 MsgSeqNum: 5 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180517-15:07:16.894 56 TargetCompID: TRADEBOTOE002 BODY 6 AvgPx: 8338.67 11 ClOrdID: 1tfX3IJi9HP87dkqlo 12 Commission: 0.000000 13 CommType: ABSOLUTE (3) 14 CumQty: 10 17 ExecID: 336933409 31 LastPx: 8338.67 32 LastQty: 10 37 OrderID: 336933405 38 OrderQty: 20 39 OrdStatus: PARTIALLY_FILLED (1) 44 Price: 8338.67 54 Side: BUY (1) 55 Symbol: BTCUSD 59 TimeInForce: IMMEDIATE_OR_CANCEL (3) 60 TransactTime: 20180517-15:07:16.892 150 ExecType: TRADE (F) 151 LeavesQty: 10 479 CommCurrency: USD 851 LastLiquidityInd: ADDED_LIQUIDITY (1) TRAILER 10 CheckSum: 001 ``` :::info **Notes**: - [AvgPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_6.html): `8338.67` is the average price of all fills on this order. - [ClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html): `1tfX3IJi9HP87dkqlo` is a unique request identifier assigned by `TRADEBOTOE002` in the [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) field in the original [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit). - [CumQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html): `10` is the total quantity of the order that is filled. - [ExecID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_17.html): `336933409` is the trade event id assigned by Gemini; see [Identifiers](/fix-overview/allowed-characters). - [ExecType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html): `TRADE (F)` indicates that this execution report is for a trade (either partial fill or fill). - [LastLiquidityInd](https://www.onixs.biz/fix-dictionary/4.4/tagNum_851.html): `ADDED_LIQUIDITY (1)` indicates that `TRADEBOTOE002` was a maker in this order. - [LastPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_31.html): `8338.67` is the price of the last fill. - [LastQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_32.html): `10` is the quantity of the partial fill. - [LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html): `10` indicates that there is a remaining quantity of 10 open for further execution. - [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html): `20` is the quantity indicated in the original [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit). - [OrdStatus](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html): `PARTIALLY_FILLED (1)` indicates that the order is partially filled. - [Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html): `8338.67` is the limit price of the order. ::: --- ### Order Cancellation In this example, `TRADEBOTOE002` receives an execution report, in response to a previously sent [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request), indicating that the order with a [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) = `GHDzdNUUXaMMDZdfwe` was cancelled: ```txt RAW 8=FIX.4.4|9=220|35=8|34=3|49=GEMINI|52=20180425-17:57:59.538|56=TRADEBOTOE002|6=0|11=GHDzdNUUXaMMDZdfwe|14=0|17=335278132|37=335278128|38=1|39=4|41=z35u64KR1gen7f2SpB|44=93392.64|54=2|55=BTCUSD|58=REQUESTED|59=1|60=20180425-17:57:59.537|150=4|151=0|10=254| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 220 34 MsgSeqNum: 3 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:57:59.538 56 TargetCompID: TRADEBOTOE002 BODY 6 AvgPx: 0 11 ClOrdID: GHDzdNUUXaMMDZdfwe 14 CumQty: 0 17 ExecID: 335278132 37 OrderID: 335278128 38 OrderQty: 1 39 OrdStatus: CANCELED (4) 41 OrigClOrdID: z35u64KR1gen7f2SpB 44 Price: 93392.64 54 Side: SELL (2) 55 Symbol: BTCUSD 58 Text: REQUESTED 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20180425-17:57:59.537 150 ExecType: CANCELED (4) 151 LeavesQty: 0 TRAILER 10 CheckSum: 254 ``` :::info **Notes**: - [AvgPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_6.html): `0` is the avg price of all fills on the order. Given that this order was cancelled before any fills, the average price is 0. - [ClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html): `GHDzdNUUXaMMDZdfwe` is a unique request identifier assigned by `TRADEBOTOE002` in the [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) field in the original [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit). - [CumQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html): `0` is the total quantity filled. Again, given that this order had no fills, this number is 0. - [ExecID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_17.html): `335278132` is an order cancellation event id assigned by Gemini; see [Identifiers](/fix-overview/allowed-characters) - [ExecType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html): `CANCELED (4)` indicates that this execution report is for a cancelled order. - [LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html): `0` represents the quantity left open for execution. In a cancelled or expired order, this quantity should equal 0. - [OrderID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_37.html): `335278128` is a globally unique order event id assigned by Gemini at the time the order is submitted. - [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html): `1` represents the number of BTC cancelled. - [OrdStatus](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html): `CANCELED (4)` indicates that the order's status is cancelled. - [OrigClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_41.html): `z35u64KR1gen7f2SpB` corresponds to the [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) of the order to cancel. - [Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html): `93392.64` is the price denominated in USD (the price currency). See [Understanding price and quantity](/trading/fix/order-entry/price-&quantity/understanding-price-&-quantity) for further explanation of price currency vs quantity currency. - [Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html): `SELL (2)` refers to the side of the order. - [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html): `BTCUSD` is the symbol name. See [Supported Symbols](/market-data/symbols-and-minimums) for a list of valid symbols and an explanation of which currencies price and quantities fields are denominated in. - [Text](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html): `REQUESTED` indicates that this execution report was for the cancellation of a GTC order where the user initiated the cancel request. - [TimeInForce](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html): `GOOD_TILL_CANCEL (1)` specifies that the order was GTC. - [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html): `20180425-17:57:59.537` is the time when the order was created. ::: In this next example, `TRADEBOTOE002` first sent an [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request) for order `1tfX3IJi9HP87dkqlo` that was previously partially filled. This is an execution report for the cancellation of the remaining quantity: ```txt RAW 8=FIX.4.4|9=238|35=8|34=7|49=GEMINI|52=20180517-15:07:16.896|56=TRADEBOTOE002|6=8338.67|11=1tfX3IJi9HP87dkqlo|14=10|17=336933412|37=336933405|38=20|39=4|44=8338.67|54=1|55=BTCUSD|59=3|60=20180517-15:07:16.892|150=4|151=0|10=080| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 238 34 MsgSeqNum: 7 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180517-15:07:16.896 56 TargetCompID: TRADEBOTOE002 BODY 6 AvgPx: 8338.67 11 ClOrdID: 1tfX3IJi9HP87dkqlo 14 CumQty: 10 17 ExecID: 336933412 37 OrderID: 336933405 38 OrderQty: 20 39 OrdStatus: CANCELED (4) 44 Price: 8338.67 54 Side: BUY (1) 55 Symbol: BTCUSD 59 TimeInForce: IMMEDIATE_OR_CANCEL (3) 60 TransactTime: 20180517-15:07:16.892 150 ExecType: CANCELED (4) 151 LeavesQty: 0 TRAILER 10 CheckSum: 080 ``` :::info **Notes**: - [CumQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html): `10` is the total quantity filled. This order had 1 fill for 10 BTC before it was cancelled. - [LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html): `0` represents the quantity left open for execution. In a cancelled or expired order, this quantity should equal 0. - [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html): `20` represents the quantity of BTC ordered in the original [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit). ::: --- ### Reject In this scenario, `TRADEBOTOE002` sent another [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit) but this time there is an error: the [Symbol `<55>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) field value is invalid. Because the symbol is invalid, the server rejects the order. This is an example of an associated execution report: ```txt RAW 8=FIX.4.4|9=237|35=8|34=2|49=GEMINI|52=20180516-22:09:05.019|56=TRADEBOTOE002|6=0|11=7v1cs7HFCT2WehadcO|14=0|17=1526508545018|37=0|38=10.4|39=8|44=0.05|54=2|55=ABCDEF|58=Unsupported Symbol value 'ABCDEF'|59=1|60=20180516-22:09:05.018|103=99|150=8|151=0|10=090| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 237 34 MsgSeqNum: 2 35 MsgType: ExecutionReport (8) 49 SenderCompID: GEMINI 52 SendingTime: 20180516-22:09:05.019 56 TargetCompID: TRADEBOTOE002 BODY 6 AvgPx: 0 11 ClOrdID: 7v1cs7HFCT2WehadcO 14 CumQty: 0 17 ExecID: 1526508545018 37 OrderID: 0 38 OrderQty: 10.4 39 OrdStatus: REJECTED (8) 44 Price: 0.05 54 Side: SELL (2) 55 Symbol: ABCDEF 58 Text: Unsupported Symbol value 'ABCDEF' 59 TimeInForce: GOOD_TILL_CANCEL (1) 60 TransactTime: 20180516-22:09:05.018 103 OrdRejReason: OTHER (99) 150 ExecType: REJECTED (8) 151 LeavesQty: 0 TRAILER 10 CheckSum: 090 ``` :::info **Notes**: - [LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html): `0` indicates that there is no quantity remaining for further execution. - [OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html): `10.4` is the quantity ordered in the original [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit). - [OrdRejReason](https://www.onixs.biz/fix-dictionary/4.4/tagNum_103.html): `OTHER (99)` suggests that there is an alternative reason for rejection. In this example, the reason for rejection is stated in the [Text `<58>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) field. - [OrdStatus](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html): `REJECTED (8)` indicates that the order is rejected. - [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html): `ABCDEF` is not a valid symbol. See [Supported Symbols](/market-data/symbols-and-minimums) for a list of valid symbols. - [Text](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html): `Unsupported Symbol value 'ABCDEF'` indicates that the symbol field's value is invalid. ::: --- URL: https://developer.gemini.com/trading/fix/order-entry/client-bound-messages/order-cancel-reject.md # Order Cancel Reject <9> # Gemini sends an [Order Cancel Reject `<9>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_9_9.html) message when the exchange receives an [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request) message which cannot be honored because: - [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request) message has an unknown [OrigClOrdID `<41>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_41.html) - the order referred to by [OrigClOrdID `<41>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_41.html) exists, but cannot be canceled because it is not currently active: - rejected - already filled - already canceled --- ### Fields | Tag | Name | Req | Description | | --- | ---------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `9` | | 11 | [ClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) | Y | Unique identifier for the order as assigned by the institution. | | 37 | [OrderID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_37.html) | Y | Unique identifier for the order as assigned by Gemini. Uniqueness is guaranteed for the trading session. The value is NONE for unknown orders. | | 39 | [OrdStatus](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) | Y | Identifies the current status of the order. The status is `8 = Rejected` if the order is unknown.

    Valid values:
    `0 = New`
    `1 = Partially filled`
    `2 = Filled`
    `4 = Canceled`
    `8 = Rejected` | | 41 | [OrigClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_41.html) | Y | The [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) of the order to cancel. Needs to correspond to a current outstanding order submitted during this trading session. | | 60 | [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html) | Y | Time the transaction represented by this [Order Cancel Reject `<9>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_9_9.html) occurred (expressed in UTC). | | 102 | [CxlRejReason](https://www.onixs.biz/fix-dictionary/4.4/tagNum_102.html) | N | Code to identify the reason for cancel rejection.

    Valid values:
    `0 = Too late to cancel`
    `1 = Unknown order`
    `3 = Order already in Pending Cancel or Pending Replace status` | | 198 | [SecondaryOrderID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_198.html) | N | Can be used to provide the order ID used by the exchange or executing system. | | 434 | [CxlRejResponseTo](https://www.onixs.biz/fix-dictionary/4.4/tagNum_434.html) | Y | Indicates the type of request that the message is in response to.

    Valid values:
    `1` = [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request) | | 58 | [Text](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) | N | | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/order-entry/client-bound-messages/order-cancel-reasons.md # Order Cancel Reasons Reasons orders are canceled include, but are not limited to: - MakerOrCancelWouldTake - A `maker-or-cancel` order would fill immediately - ImmediateOrCancelWouldPost - An `immediate-or-cancel` order would not fill immediately - FillOrKillWouldNotFill - A `fill-or-kill` order would not fully fill immediately - ExceedsPriceLimits - Exceeds price limits imposed by Gemini - SelfCrossPrevented - Crosses a pre-existing open order - MarketOrderSweptBook - Unable to completely fill the `market` order --- URL: https://developer.gemini.com/trading/fix/order-entry/client-bound-messages/execution-report.md # Execution Report <8> # Gemini uses the [Execution Report `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) message to: - confirm the receipt of an order - confirm a stop order has triggered - confirm the successful cancellation of an order - relay fill information on orders - reject orders Each execution report contains two fields which are used to communicate both the current state of the order as understood by the exchange ([OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html)) and the purpose of the message ([ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html)). --- ### Fields `](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) of the order to cancel. Needs to correspond to a current outstanding order submitted during this trading session.\n\n \u00A0 \n\n\\*Required for a response to an [Order Cancel Request ``](/trading/fix/order-entry/exchange-bound-messages/order-cancel-request).", ], [ "17", "[ExecID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_17.html)", "Y", "Unique event ID assigned by Gemini.", ], [ "150", "[ExecType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html)", "Y", "Describes the purpose of the Execution Report.\n\n \u00A0 \n\nValid values:\n\n`0 = New`\n\n`F = Trade`\n\n`4 = Canceled`\n\n`8 = Rejected`", ], [ "18", "[ExecInst](https://www.onixs.biz/fix-dictionary/4.4/tagNum_18.html)", "N", "Indicates if an order was Maker-or-Cancel.\n\n \u00A0 \n\nValid values:\n\n`6 = Participate don't initiate (maker-or-cancel)`", ], [ "39", "[OrdStatus](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html)", "Y", "Describes the current order status.\n\n \u00A0 \n\nValid values:\n\n`0 = New`\n\n`1 = Partially filled`\n\n`2 = Filled`\n\n`4 = Canceled`\n\n`8 = Rejected`", ], [ "55", "[Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html)", "Y", "Ticker symbol of the order.\n\n \u00A0 \n\nSee [Supported Symbols](/market-data/symbols-and-minimums) for valid values.", ], [ "54", "[Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html)", "Y", "Side of the order.\n\n \u00A0 \n\nValid values:\n\n`1 = Buy`\n\n`2 = Sell`", ], [ "44", "[Price](https://www.onixs.biz/fix-dictionary/4.4/tagNum_44.html)", "N\\*", "Limit price of the order.\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is not `8 = Rejected` and if specified on the order.", ], [ "99", "[StopPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_99.html)", "N\\*", "Stop price of the order.\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is not `8 = Rejected` and responding to an [OrdType `<40>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_40.html) is `4 = Stop Limit` order entry. \n`StopPx` must be ≤ `Price` if `Side <54>` is `1 = Buy`, and ≥ `Price` if `Side <54>` is `2 = Sell`.", ], [ "6", "[AvgPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_6.html)", "Y", "Calculated average price of fills on this order. Zero for an order with no fills.", ], [ "31", "[LastPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_31.html)", "N\\*", "Price of the fill. This field is only present when the order is updated due to a match on the exchange.\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is `F = Trade`.", ], [ "14", "[CumQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html)", "Y", "Total quantity of the order that is filled.", ], [ "38", "[OrderQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html)", "N\\*", "Decimal amount of BTC to purchase. The general rule is: [OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html) = [CumQty `<14>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html) + [LeavesQty `<151>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html).\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is not `8 = Rejected`.", ], [ "151", "[LeavesQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html)", "Y", "[Quantity `<53>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_53.html) open for further execution. If [OrdStatus `<39>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_39.html) is `4 = Canceled` or `8 = Rejected` (making the order no longer active), then [LeavesQty `<151>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html) could be 0. Otherwise, [LeavesQty `<151>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_151.html) = [OrderQty `<38>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_38.html) - [CumQty `<14>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_14.html).", ], [ "32", "[LastQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_32.html)", "N\\*", "Quantity of the fill. This field is only present when the order is updated due to a match on the exchange.\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is `F = Trade`.", ], [ "12", "[Commission](https://www.onixs.biz/fix-dictionary/4.4/tagNum_12.html)", "N\\*", "Fee charged for the trade (negative for rebates).\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is `F = Trade`.", ], [ "479", "[CommCurrency](https://www.onixs.biz/fix-dictionary/4.4/tagNum_479.html)", "N\\*", "[Currency code](https://www.onixs.biz/fix-dictionary/4.4/app_6_a.html) of the currency that the fee is denominated in.\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is `F = Trade`.", ], [ "13", "[CommType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_13.html)", "N\\*", "Commission type.\n\n \u00A0 \n\nValid value:\n\n`3 = absolute` (total monetary amount)\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is `F = Trade`.", ], [ "851", "[LastLiquidityInd](https://www.onixs.biz/fix-dictionary/4.4/tagNum_851.html)", "N\\*", 'Whether this order added liquidity ("maker") or removed liquidity ("taker").\n\n \u00A0 \n\nValid values:\n\n`1 = Added Liquidity`\n\n`2 = Removed Liquidity`\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is `F = Trade`.', ], [ "103", "[OrdRejReason](https://www.onixs.biz/fix-dictionary/4.4/tagNum_103.html)", "N\\*", "Reason the order was rejected.\n\n \u00A0 \n\nValid values:\n\n`1 = Unknown symbol`\n\n`2 = Exchange closed for scheduled maintenance`\n\n`3 = Order exceeds limit`\n\n`13 = Incorrect quantity`\n\n`99 = Other`\n\n \u00A0 \n\n\\*Required if [ExecType `<150>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_150.html) is `8 = Rejected`.", ], [ "58", "[Text](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html)", "N\\*", 'Reason for order rejection or [cancellation](/trading/fix/order-entry/client-bound-messages/order-cancel-reasons). For a Cancel Ack, this field will only be populated with "Requested" in response to explicit Order Cancel requests (`35=F`). Unsolicited cancel acknowledgments may not include this field.\n\n \u00A0 \n\n\\*Required if [OrdRejReason `<103>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_103.html) is `99 = Other`.', ], [ "59", "[TimeInForce](https://www.onixs.biz/fix-dictionary/4.4/tagNum_59.html)", "Y\\*", "Specifies how long the order remains in effect. Absence of this field would be interpreted as a day order, which Gemini does not currently support.\n\n \u00A0 \n\nValid values:\n\n`1 = Good Till Cancel (GTC)`\n\n`3 = Immediate Or Cancel (IOC)`\n\n`4 = Fill Or Kill (FOK)`", ], [ "60", "[TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html)", "Y", "Time the transaction represented by this [ExecutionReport `<8>`](https://www.onixs.biz/fix-dictionary/4.4/msgType_8_8.html) occurred (expressed in UTC).", ], ["", "[Standard Trailer](/trading/fix/overview/standard-trailer)", "Y", ""], ]} /> --- URL: https://developer.gemini.com/trading/fix/market-data/workflow/understanding-price-and-quantity.md # Understanding price and quantity In a [Market Data - Incremental Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) message, the [MDEntryPx `<270>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_270.html) field is denominated in USD and the [MDEntrySize `<271>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_271.html) field is denominated in BTC. ```txt RAW 8=FIX.4.4|9=122|35=X|34=41|49=GEMINI|52=20160217-18:07:26.581|56=CLIENT|262=TestMDReqID_1|268=1|279=0|269=0|55=BTCUSD|270=300.43|271=0.57|10=070| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 122 34 MsgSeqNum: 41 35 MsgType: MarketDataIncrementalRefresh (X) 49 SenderCompID: GEMINI 52 SendingTime: 20160217-18:07:26.581 56 TargetCompID: CLIENT BODY 262 MDReqID: TestMDReqID_1 268 NoMDEntries: count = 1 55 Symbol: BTCUSD 269 MDEntryType: BID (0) 270 MDEntryPx: 419.02 271 MDEntrySize: 9.50 279 MDUpdateAction: NEW (0) TRAILER 10 CheckSum: 070 ``` --- URL: https://developer.gemini.com/trading/fix/market-data/workflow/symbol-list.md # Symbol list _Optional_: Gemini offers a symbol list that supplies the definitive list of symbols traded on the exchange. 1. Client sends a [Symbol List Request ``](/trading/fix/market-data/exchange-bound-messages/symbol-list-request) 2. Gemini responds with a [Symbol List ``](/trading/fix/market-data/exchange-bound-messages/symbol-list-request) --- URL: https://developer.gemini.com/trading/fix/market-data/workflow/market-data-subscription.md # Market data subscription After connecting to the market data channel, the client subscribes to market data by sending a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) message. When a client disconnects for any reason, the market data subscription is terminated. Upon reconnecting, the client needs to send another [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) message. 1. Client sends a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) 2. Gemini responds by sending one [Market Data - Snapshot/Full Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-snapshot-full-refresh) 3. As bids, offers, and trades happen on the exchange, Gemini sends [Market Data - Incremental Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) messages --- URL: https://developer.gemini.com/trading/fix/market-data/workflow/currency-based-messages-and-fields.md # Currency-based messages and fields `](/trading/fix/market-data/client-bound-messages/market-data-snapshot-full-refresh)", "[MDEntryPx `<270>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_270.html)", "[MDEntrySize `<271>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_271.html)", ], [ "[Market Data - Incremental Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh)", "[MDEntryPx `<270>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_270.html)", "[MDEntrySize `<271>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_271.html)", ], ]} /> --- URL: https://developer.gemini.com/trading/fix/market-data/workflow/about.md # About After connecting and logging on, the client can either request a symbol list or subscribe to market data. Gemini does not resend messages on a market data channel. Instead, Gemini will send a [Sequence Reset `<4>`](https://www.onixs.biz/fix-dictionary/4.4/msgtype_4_4.html) message with [GapFillFlag `<123>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_123.html) field set to `Y` and the new sequence number in the [MsgSeqNum `<34>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_34.html) field. --- URL: https://developer.gemini.com/trading/fix/market-data/identifiers/identifiers-assigned-by-gemini.md # Identifiers assigned by Gemini Market data messages do not have any identifiers assigned by Gemini. --- URL: https://developer.gemini.com/trading/fix/market-data/identifiers/client-supplied-identifiers.md # Client supplied identifiers ### Identifiers `](/trading/fix/market-data/exchange-bound-messages/symbol-list-request)", "[Symbol List ``](/trading/fix/market-data/client-bound-messages/symbol-list)", "The client's symbol list request identifier. Gemini does not enforce uniqueness although the FIX protocol recommends it.", ], [ "262", "[MDReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_262.html)", "[Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request)", "[Market Data – Snapshot/Full Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-snapshot-full-refresh)\n\n \u00A0 \n\n[Market Data – Incremental Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh)\n\n \u00A0 \n\n[Market Data Request Reject ``](/trading/fix/market-data/client-bound-messages/market-data-request-reject)", "The client's market data request identifier. Gemini requires uniqueness per active FIX session. A [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) message with a duplicate [MDReqID `<262>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_262.html) value during the current active FIX session will be rejected using a [Market Data Request Reject ``](/trading/fix/market-data/client-bound-messages/market-data-request-reject) with the [MDReqRejReason `<281>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_281.html) field set to `1` = Duplicate [MDReqID `<262>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_262.html).", ], ]} /> --- URL: https://developer.gemini.com/trading/fix/market-data/exchange-bound-messages/symbol-list-request.md # Symbol List Request # A [Symbol List Request ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_x_120.html) returns a [Symbol List ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_y_121.html) response containing the symbols traded on the exchange. See [Examples: Request for the symbol list](/trading/fix/market-data/examples/request-for-symbol-list) for sample requests and responses. --- ### Fields | Tag | Name | Req | Description | | --- | ----------------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `x` | | 320 | [SecurityReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_320.html) | Y | Unique identifier of this request. | | 559 | [SecurityListRequestType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_559.html) | Y | The type/criteria of the request. | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | Valid value:
    `0 = Symbol` | --- URL: https://developer.gemini.com/trading/fix/market-data/exchange-bound-messages/market-data-request.md # Market Data Request # Subscribes the current session to a [Market Data - Snapshot/Full Refresh ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_W_87.html) followed by zero or more [Market Data - Incremental Refresh ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_X_88.html) messages. See [Examples: Market Data Requests](/trading/fix/market-data/examples/market-data-requests) for sample requests and responses. --- ### Fields | Tag | Name | Req | Description | | ----- | ----------------------------------------------------------------------------------- | --- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `V` | | 262 | [MDReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_262.html) | Y | Unique identifier of the market data request. Uniqueness must be guaranteed by the institution for the duration of the connection to the market data channel. | | 263 | [SubscriptionRequestType](https://www.onixs.biz/fix-dictionary/4.2/tagNum_263.html) | Y | Indicates what type of response is expected.

    Valid values:
    `1 = Snapshot + Updates (Subscribe)` | | 264 | [MarketDepth](https://www.onixs.biz/fix-dictionary/4.4/tagNum_264.html) | Y | Depth of the book to receive snapshot and updates for.

    Valid values:
    `0 = Full Book`
    `1 = Top of Book` | | 267 | [NoMDEntryTypes](https://www.onixs.biz/fix-dictionary/4.4/tagNum_267.html) | Y | Number of [MDEntryType `<269>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) fields requested. | | ⇒ 269 | [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) | Y | Type of market data entry to receive snapshots and updates for.

    Valid values:
    `0 = Bid`
    `1 = Offer`
    `2 = Trade`
    `R = MarkPrice`
    `S = FundingAmount` | | 146 | [NoRelatedSym](https://www.onixs.biz/fix-dictionary/4.4/tagNum_146.html) | Y | Number of symbols requested. | | ⇒ 55 | [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) | Y | Market data symbol requested.

    See [Symbol List ``](/trading/fix/market-data/client-bound-messages/symbol-list) for a list of supported symbols. | | 9003 | [EnableMDEntryMakerSide](/trading/fix/overview/dictionary/custom-tags) | N | Optional custom field to enable showing [MDEntryMakerSide `<9002>`](/trading/fix/overview/dictionary/custom-tags) in [Market Data – Incremental Refresh](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) messages when [MDEntryType `<269>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) had the value `2 = Trade`.

    See [Examples: Request to enable maker side on trades](/trading/fix/market-data/examples/market-data-requests). | | 9009 | [MDEntryFundingIsRealized](/trading/fix/overview/dictionary/custom-tags) | N | Optional custom field used in [Market Data – Incremental Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) MDEntry groups when [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) had the value `S = Funding Amount` to indicate the `IsRealized` field of Funding Amount. | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/market-data/examples/request-for-symbol-list.md # Request Symbol List ### Request This is a request for the list of symbols and its response: ```txt RAW 8=FIX.4.4|9=74|35=x|34=2|49=TRADEBOTMD002|52=20180425-17:51:27.000|56=GEMINI|320=1|559=0|10=099| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 74 34 MsgSeqNum: 2 35 MsgType: SymbolListRequest (x) 49 SenderCompID: TRADEBOTMD002 52 SendingTime: 20180425-17:51:27.000 56 TargetCompID: GEMINI BODY 320 SecurityReqID: 1 559 SecurityListRequestType: SYMBOL (0) TRAILER 10 CheckSum: 099 ``` ### Response ```txt RAW 8=FIX.4.4|9=146|35=y|34=2|49=GEMINI|52=20200425-17:51:27.544|56=TRADEBOTMD002|320=1|322=1|560=0|146=6|55=BTCUSD|55=ETHBTC|55=ETHUSD|55=BCHUSD|55=LTCUSD|55=LTCBTC|55=LTCETH|55=BATUSD|55=DAIUSD|55=LINKUSD|55=OXTUSD|55=LINKBTC|55=LINKETH|10=054| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 146 34 MsgSeqNum: 2 35 MsgType: SecurityList (y) 49 SenderCompID: GEMINI 52 SendingTime: 20200425-17:51:27.544 56 TargetCompID: TRADEBOTMD002 BODY 320 SecurityReqID: 1 322 SecurityResponseID: 1 560 SecurityRequestResult: VALID_REQUEST (0) 146 NoRelatedSym: count = 3 55 Symbol: BTCUSD ---- 55 Symbol: ETHBTC ---- 55 Symbol: ETHUSD ---- 55 Symbol: BCHUSD ---- 55 Symbol: LTCUSD ---- 55 Symbol: LTCBTC ---- 55 Symbol: LTCETH ---- 55 Symbol: BATUSD ---- 55 Symbol: DAIUSD ---- 55 Symbol: LINKUSD ---- 55 Symbol: OXTUSD ---- 55 Symbol: LINKBTC ---- 55 Symbol: LINKETH TRAILER 10 CheckSum: 054 ``` --- URL: https://developer.gemini.com/trading/fix/market-data/examples/market-data-responses.md # Market Data Responses These are some examples of FIX market data sent from GEMINIMKT to TESTMKT001 with price, liquidity, and trade information. Note that this is public data, so no assumption about the trade’s counterparty should be made. --- ## Full Snapshot The initial response to a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) is a [Market Data - Snapshot / Full Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-snapshot-full-refresh) message containing a full snapshot of current state. ```txt HEADER 8 BeginString: FIX.4.4 9 BodyLength: 19924 34 MsgSeqNum: 4 35 MsgType: MarketDataSnapshotFullRefresh (W) 49 SenderCompID: GEMINIMKT 52 SendingTime: 20180121-03:48:39.102 56 TargetCompID: TESTMKT001 BODY 55 Symbol: ETHBTC 262 MDReqID: 39 268 NoMDEntries: count = 685 269 MDEntryType: BID (0) 270 MDEntryPx: 0.00001 271 MDEntrySize: 10111 ---- 269 MDEntryType: BID (0) 270 MDEntryPx: 0.08989 271 MDEntrySize: 8.918 ---- 269 MDEntryType: BID (0) 270 MDEntryPx: 0.0899 271 MDEntrySize: 8.8828 ---- 269 MDEntryType: OFFER (1) 270 MDEntryPx: 0.09032 271 MDEntrySize: 45.134 ---- 269 MDEntryType: OFFER (1) 270 MDEntryPx: 0.09049 271 MDEntrySize: 1.2 ---- 269 MDEntryType: OFFER (1) 270 MDEntryPx: 1000.00000 271 MDEntrySize: 3 TRAILER 10 CheckSum: 084 ``` --- ## Quantity ### Adding Quantity For a Given Price This message shows a new bid for 2.8749 ETH priced at 988.88 USD. ```txt HEADER 8 BeginString: FIX.4.4 9 BodyLength: 125 34 MsgSeqNum: 5696449 35 MsgType: MarketDataIncrementalRefresh (X) 49 SenderCompID: GEMINIMKT 52 SendingTime: 20180123-04:07:42.101 56 TargetCompID: TESTMKT001 BODY 262 MDReqID: 40 9008 EventId: 123456789 268 NoMDEntries: count = 1 55 Symbol: ETHUSD 269 MDEntryType: BID (0) 270 MDEntryPx: 988.88 271 MDEntrySize: 2.8749 279 MDUpdateAction: NEW (0) TRAILER 10 CheckSum: 187 ``` ### Changing Quantity For a Given Price This message shows a new offer for 0.20503505 BTC available at price 10949.04 USD. ```txt HEADER 8 BeginString: FIX.4.4 9 BodyLength: 131 34 MsgSeqNum: 5696384 35 MsgType: MarketDataIncrementalRefresh (X) 49 SenderCompID: GEMINIMKT 52 SendingTime: 20180123-04:07:40.057 56 TargetCompID: TESTMKT001 BODY 262 MDReqID: 38 9008 EventId: 123456789 268 NoMDEntries: count = 1 55 Symbol: BTCUSD 269 MDEntryType: OFFER (1) 270 MDEntryPx: 10949.04 271 MDEntrySize: 0.20503505 279 MDUpdateAction: CHANGE (1) TRAILER 10 CheckSum: 199 ``` ### Removing Quantity For a Given Price This message shows an ETHBTC bid priced at 0.09156 BTC being removed. ```txt HEADER 8 BeginString: FIX.4.4 9 BodyLength: 115 34 MsgSeqNum: 5696454 35 MsgType: MarketDataIncrementalRefresh (X) 49 SenderCompID: GEMINIMKT 52 SendingTime: 20180123-04:07:42.279 56 TargetCompID: TESTMKT001 BODY 262 MDReqID: 39 9008 EventId: 123456789 268 NoMDEntries: count = 1 55 Symbol: ETHBTC 269 MDEntryType: BID (0) 270 MDEntryPx: 0.09156 279 MDUpdateAction: DELETE (2) TRAILER 10 CheckSum: 197 ``` --- ## Trades This message provides information about a new trade and a corresponding offer deletion. This message implies that the incoming order was a bid because the standing offer priced at 10907.54 USD was deleted. ```txt HEADER 8 BeginString: FIX.4.4 9 BodyLength: 166 34 MsgSeqNum: 5696411 35 MsgType: MarketDataIncrementalRefresh (X) 49 SenderCompID: GEMINIMKT 52 SendingTime: 20180123-04:07:40.740 56 TargetCompID: TESTMKT001 BODY 262 MDReqID: 38 9008 EventId: 123456789 268 NoMDEntries: count = 2 55 Symbol: BTCUSD 269 MDEntryType: TRADE (2) 270 MDEntryPx: 10907.54 271 MDEntrySize: 0.00059578 279 MDUpdateAction: NEW (0) ---- 55 Symbol: BTCUSD 269 MDEntryType: OFFER (1) 270 MDEntryPx: 10907.54 279 MDUpdateAction: DELETE (2) TRAILER 10 CheckSum: 209 ``` --- ## Showing Maker Side To enable showing the maker side of trades, create a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) with custom field [EnableMDEntryMakerSide `<9003>`](/trading/fix/overview/dictionary/custom-tags) enabled - see [Examples: Request to enable maker side on trades](/trading/fix/market-data/examples/market-data-requests). ### Offer Hit Standing Bid This example shows a trade where an incoming offer hit a standing bid at for 0.001 BTC at 7544.94 USD. ```txt RAW 8=FIX.4.4|9=125|35=X|34=3|49=GEMINI|52=20180809-15:59:16.698|56=TRADEBOTMD002|262=2|9008=123456789|268=1|279=0|269=2|55=BTCUSD|270=7544.94|271=0.001|9002=1|10=107| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 125 34 MsgSeqNum: 3 35 MsgType: MarketDataIncrementalRefresh (X) 49 SenderCompID: GEMINI 52 SendingTime: 20180809-15:59:16.698 56 TargetCompID: TRADEBOTMD002 BODY 262 MDReqID: 2 9008 EventId: 123456789 268 NoMDEntries: count = 1 55 Symbol: BTCUSD 269 MDEntryType: TRADE (2) 270 MDEntryPx: 7544.94 271 MDEntrySize: 0.001 279 MDUpdateAction: NEW (0) 9002 MDEntryMakerSide: BUY (1) TRAILER 10 CheckSum: 107 ``` ### Bid Lifted Standing Offer This example shows a trade where an incoming bid lifted a standing offer at for 0.001 BTC at 7549.89 USD. ```txt RAW 8=FIX.4.4|9=125|35=X|34=3|49=GEMINI|52=20180809-15:59:22.882|56=TRADEBOTMD002|262=2|9008=123456789|268=1|279=0|269=2|55=BTCUSD|270=7549.89|271=0.001|9002=2|10=109| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 125 34 MsgSeqNum: 3 35 MsgType: MarketDataIncrementalRefresh (X) 49 SenderCompID: GEMINI 52 SendingTime: 20180809-15:59:22.882 56 TargetCompID: TRADEBOTMD002 BODY 262 MDReqID: 2 9008 EventId: 123456789 268 NoMDEntries: count = 1 55 Symbol: BTCUSD 269 MDEntryType: TRADE (2) 270 MDEntryPx: 7549.89 271 MDEntrySize: 0.001 279 MDUpdateAction: NEW (0) 9002 MDEntryMakerSide: SELL (2) TRAILER 10 CheckSum: 109 ``` --- ## Rejections ### Rejected Symbol ```txt RAW 8=FIX.4.4|9=67|35=Y|34=2|49=GEMINIMKT|52=20180511-21:37:57.971|56=TESTMKT001|262=badsym|281=0|10=038| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 67 34 MsgSeqNum: 2 35 MsgType: MarketDataRequestReject (Y) 49 SenderCompID: GEMINIMKT 52 SendingTime: 20180511-21:37:57.971 56 TargetCompID: TESTMKT001 BODY 262 MDReqID: badsym 281 MDReqRejReason: UNKNOWN_SYMBOL (0) TRAILER 10 CheckSum: 03 ``` ### Rejected MD Entry Type ```txt RAW 8=FIX.4.4|9=70|35=Y|34=2|49=GEMINIMKT|52=20180511-21:44:14.765|56=TESTMKT001|262=badmd|281=8|10=121| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 70 34 MsgSeqNum: 2 35 MsgType: MarketDataRequestReject (Y) 49 SenderCompID: GEMINIMKT 52 SendingTime: 20180511-21:44:14.765 56 TargetCompID: TESTMKT001 BODY 262 MDReqID: badmd 281 MDReqRejReason: UNSUPPORTED_MDENTRYTYPE (8) TRAILER 10 CheckSum: 121 ``` ### Rejected Subscription Request ```txt RAW 8=FIX.4.4|9=67|35=Y|34=2|49=GEMINIMKT|52=20180514-13:59:56.187|56=TESTMKT001|262=badsub|281=4|10=048| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 67 34 MsgSeqNum: 2 35 MsgType: MarketDataRequestReject (Y) 49 SenderCompID: GEMINIMKT 52 SendingTime: 20180514-13:59:56.187 56 TargetCompID: TESTMKT001 BODY 262 MDReqID: badsub 281 MDReqRejReason: UNSUPPORTED_SUBSCRIPTIONREQUESTTYPE (4) TRAILER 10 CheckSum: 048 ``` --- URL: https://developer.gemini.com/trading/fix/market-data/examples/market-data-requests.md # Market Data Requests These are some examples of FIX market data requests. These requests only need to be made once per session to setup a FIX connection for market data. :::warning Notes: - [SubscriptionRequestType `<263>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_263.html) = `1 SNAPSHOT_PLUS_UPDATES` allows the sender to subscribe to the data feed and receive updates. ::: --- ## Top of Book (Bids) This is a request for the Top of Book BTCUSD bids and its response: ### Request ```txt RAW 8=FIX.4.4|9=114|35=V|34=2|49=TRADEBOTMD002|52=20180425-17:51:40.000|56=GEMINI|262=2|263=1|264=1|146=1|55=BTCUSD|267=1|269=0|10=016| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 114 34 MsgSeqNum: 2 35 MsgType: MarketDataRequest (V) 49 SenderCompID: TRADEBOTMD002 52 SendingTime: 20180425-17:51:40.000 56 TargetCompID: GEMINI BODY 262 MDReqID: 2 263 SubscriptionRequestType: SNAPSHOT_PLUS_UPDATES (1) 264 MarketDepth: TOP_OF_BOOK (1) 146 NoRelatedSym: count = 1 55 Symbol: BTCUSD 267 NoMDEntryTypes: count = 1 269 MDEntryType: BID (0) TRAILER 10 CheckSum: 016 ``` ### Response ```txt RAW 8=FIX.4.4|9=108|35=W|34=2|49=GEMINI|52=20180425-17:51:40.787|56=TRADEBOTMD002|55=BTCUSD|262=2|268=1|269=0|270=8490.07|271=1|10=075| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 108 34 MsgSeqNum: 2 35 MsgType: MarketDataSnapshotFullRefresh (W) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:51:40.787 56 TargetCompID: TRADEBOTMD002 BODY 55 Symbol: BTCUSD 262 MDReqID: 2 268 NoMDEntries: count = 1 269 MDEntryType: BID (0) 270 MDEntryPx: 8490.07 271 MDEntrySize: 1 TRAILER 10 CheckSum: 075 ``` --- ## Trades This is a request for all ETHBTC trades: ### Request ```txt RAW 8=FIX.4.4|9=114|35=V|34=2|49=TRADEBOTMD002|52=20180425-17:55:30.000|56=GEMINI|262=2|263=1|264=0|146=1|55=ETHBTC|267=1|269=2|10=009| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 114 34 MsgSeqNum: 2 35 MsgType: MarketDataRequest (V) 49 SenderCompID: TRADEBOTMD002 52 SendingTime: 20180425-17:55:30.000 56 TargetCompID: GEMINI BODY 262 MDReqID: 2 263 SubscriptionRequestType: SNAPSHOT_PLUS_UPDATES (1) 264 MarketDepth: FULL_BOOK (0) 146 NoRelatedSym: count = 1 55 Symbol: ETHBTC 267 NoMDEntryTypes: count = 1 269 MDEntryType: TRADE (2) TRAILER 10 CheckSum: 009 ``` ### Request - Multiple Symbols This is a request for the Top of Book BTCUSD, ETHUSD, and ETHBTC bids: ```txt RAW 8=FIX.4.4|9=134|35=V|34=2|49=TRADEBOTMD002|52=20180425-17:55:38.000|56=GEMINI|262=2|263=1|264=1|146=3|55=BTCUSD|55=ETHUSD|55=ETHBTC|267=1|269=0|10=246| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 134 34 MsgSeqNum: 2 35 MsgType: MarketDataRequest (V) 49 SenderCompID: TRADEBOTMD002 52 SendingTime: 20180425-17:55:38.000 56 TargetCompID: GEMINI BODY 262 MDReqID: 2 263 SubscriptionRequestType: SNAPSHOT_PLUS_UPDATES (1) 264 MarketDepth: TOP_OF_BOOK (1) 146 NoRelatedSym: count = 3 55 Symbol: BTCUSD ---- 55 Symbol: ETHUSD ---- 55 Symbol: ETHBTC 267 NoMDEntryTypes: count = 1 269 MDEntryType: BID (0) TRAILER 10 CheckSum: 246 ``` ### Responses and the corresponding responses: ```txt RAW 8=FIX.4.4|9=108|35=W|34=2|49=GEMINI|52=20180425-17:55:39.151|56=TRADEBOTMD002|55=BTCUSD|262=2|268=1|269=0|270=8490.07|271=1|10=072| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 108 34 MsgSeqNum: 2 35 MsgType: MarketDataSnapshotFullRefresh (W) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:55:39.151 56 TargetCompID: TRADEBOTMD002 BODY 55 Symbol: BTCUSD 262 MDReqID: 2 268 NoMDEntries: count = 1 269 MDEntryType: BID (0) 270 MDEntryPx: 8490.07 271 MDEntrySize: 1 TRAILER 10 CheckSum: 072 ``` and: ```txt RAW 8=FIX.4.4|9=107|35=W|34=3|49=GEMINI|52=20180425-17:55:39.151|56=TRADEBOTMD002|55=ETHUSD|262=2|268=1|269=0|270=587.35|271=1|10=032| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 107 34 MsgSeqNum: 3 35 MsgType: MarketDataSnapshotFullRefresh (W) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:55:39.151 56 TargetCompID: TRADEBOTMD002 BODY 55 Symbol: ETHUSD 262 MDReqID: 2 268 NoMDEntries: count = 1 269 MDEntryType: BID (0) 270 MDEntryPx: 587.35 271 MDEntrySize: 1 TRAILER 10 CheckSum: 032 ``` and: ```txt RAW 8=FIX.4.4|9=115|35=W|34=4|49=GEMINI|52=20180425-17:55:39.152|56=TRADEBOTMD002|55=ETHBTC|262=2|268=1|269=0|270=0.06896|271=0.004008|10=152| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 115 34 MsgSeqNum: 4 35 MsgType: MarketDataSnapshotFullRefresh (W) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:55:39.152 56 TargetCompID: TRADEBOTMD002 BODY 55 Symbol: ETHBTC 262 MDReqID: 2 268 NoMDEntries: count = 1 269 MDEntryType: BID (0) 270 MDEntryPx: 0.06896 271 MDEntrySize: 0.004008 TRAILER 10 CheckSum: 152 ``` --- ## Enable Maker Side on Trades To enable showing the maker side of trades, create a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) with custom field [EnableMDEntryMakerSide `<9003>`](/trading/fix/overview/dictionary/custom-tags) enabled. When trades occur, custom field [MDEntryMakerSide `<9002>`](/trading/fix/overview/dictionary/custom-tags) will appear in the MDEntry group with the market side of the trade. See [Examples: Showing Maker Side For Trades](/trading/fix/market-data/examples/market-data-responses). ```txt RAW 8=FIX.4.4|9=121|35=V|34=2|49=TRADEBOTMD002|52=20180809-15:59:14.000|56=GEMINI|262=2|263=1|264=1|9003=Y|146=1|55=BTCUSD|267=1|269=2|10=128| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 121 34 MsgSeqNum: 2 35 MsgType: MarketDataRequest (V) 49 SenderCompID: TRADEBOTMD002 52 SendingTime: 20180809-15:59:14.000 56 TargetCompID: GEMINI BODY 262 MDReqID: 2 263 SubscriptionRequestType: SNAPSHOT_PLUS_UPDATES (1) 264 MarketDepth: TOP_OF_BOOK (1) 9003 EnableMDEntryMakerSide: Y 146 NoRelatedSym: count = 1 55 Symbol: BTCUSD 267 NoMDEntryTypes: count = 1 269 MDEntryType: TRADE (2) TRAILER 10 CheckSum: 128 ``` --- URL: https://developer.gemini.com/trading/fix/market-data/client-bound-messages/symbol-list.md # Symbol List # A [Symbol List ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_y_121.html) is the response containing the list of symbols specified in a [Symbol List Request ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_x_120.html). See [Examples: Request for the symbol list](/trading/fix/market-data/examples/request-for-symbol-list) for sample requests and responses. --- ### Fields | Tag | Name | Req | Description | | ---- | --------------------------------------------------------------------------------- | --- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `y` | | 320 | [SecurityReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_320.html) | Y | Unique identifier of the [Symbol List Request ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_x_120.html) that solicited this response. | | 322 | [SecurityResponseID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_322.html) | Y | Identifier for this message. | | 560 | [SecurityRequestResult](https://www.onixs.biz/fix-dictionary/4.4/tagNum_560.html) | Y | The result of this request.

    Valid values:
    `0 = Valid Request`
    `1 = Invalid Request` | | 146 | [NoRelatedSym](https://www.onixs.biz/fix-dictionary/4.4/tagNum_146.html) | Y | Specifies the number of returned symbols. | | ⇒ 55 | [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) | Y | Symbol of exchange-traded order book pair. | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/market-data/client-bound-messages/market-data-snapshot-full-refresh.md # Market Data - Snapshot / Full Refresh # The initial response to a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) is a full snapshot of current state followed by multiple [Market Data - Incremental Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) messages. See [Examples: Full Snapshot](/trading/fix/market-data/examples/market-data-responses). --- ### Fields | Tag | Name | Req | Description | | ----- | ----------------------------------------------------------------------- | --- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `W` | | 262 | [MDReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_262.html) | Y | Unique identifier of the [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) this message is in response to. | | 55 | [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) | Y | Symbol of market data entry. | | 268 | [NoMDEntries](https://www.onixs.biz/fix-dictionary/4.4/tagNum_268.html) | Y | Number of entries in this message. | | ⇒ 269 | [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) | Y | Type of market data update.

    Valid values:
    `0 = Bid`
    `1 = Offer`
    `2 = Trade` | | ⇒ 270 | [MDEntryPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_270.html) | Y | Price of market data entry. | | ⇒ 271 | [MDEntrySize](https://www.onixs.biz/fix-dictionary/4.4/tagNum_271.html) | Y | Quantity of market data entry. | | ⇒ 273 | [MDEntryTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_273.html) | N\* | The time that the most recent indicative price has been published. | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/market-data/client-bound-messages/market-data-request-reject.md # Market Data Request Reject # ### Fields | Tag | Name | Req | Description | | --- | -------------------------------------------------------------------------- | --- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `Y` | | 262 | [MDReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_262.html) | Y | Unique identifier of the market data request being rejected. | | 281 | [MDReqRejReason](https://www.onixs.biz/fix-dictionary/4.4/tagNum_281.html) | N | Reason why [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) was rejected.

    Valid values:
    `0 = Unknown Symbol`
    `1` = Duplicate [MDReqID `<262>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_262.html)
    `4` = Unsupported [SubscriptionRequestType `<263>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_263.html)
    `5` = Unsupported [MarketDepth `<264>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_264.html)
    `8` = Unsupported [MDEntryType `<269>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh.md # Market Data - Incremental Refresh # The initial response to a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) is a full snapshot of current state followed by multiple incremental update messages. See [Examples: Market Data Responses](/trading/fix/market-data/examples/market-data-responses) for examples of bids, offers, and trade events. --- ### Fields | Tag | Name | Req | Description | | ------ | -------------------------------------------------------------------------- | --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | | [Standard Header](/trading/fix/overview/standard-header) | Y | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `X` | | 262 | [MDReqID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_262.html) | Y | Unique identifier of the [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) this message is in response to. | | 9008 | [EventId](/trading/fix/overview/dictionary/custom-tags) | N | Event ID of the event causing the generation of the update in this message. | | 268 | [NoMDEntries](https://www.onixs.biz/fix-dictionary/4.4/tagNum_268.html) | Y | Number of entries in this message. | | ⇒ 279 | [MDUpdateAction](https://www.onixs.biz/fix-dictionary/4.4/tagNum_279.html) | Y | Type of market data update.

    Valid values:
    `0 = New`
    `1 = Change`
    `2 = Delete` | | ⇒ 269 | [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) | Y | Type of market data entry.

    Valid values:
    `0 = Bid`
    `1 = Offer`
    `2 = Trade`
    `R = MarkPrice`
    `S = FundingAmount` | | ⇒ 55 | [Symbol](https://www.onixs.biz/fix-dictionary/4.4/tagNum_55.html) | N | Symbol of market data entry.

    Required when [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) is not `3 = Index Value`. | | ⇒ 270 | [MDEntryPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_270.html) | Y | Price of market data entry. | | ⇒ 271 | [MDEntrySize](https://www.onixs.biz/fix-dictionary/4.4/tagNum_271.html) | N\* | Quantity of market data entry.

    \*Required when [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) is not `3 = Index Value`. | | ⇒ 273 | [MDEntryTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_273.html) | N\* | The time that the most recent indicative price has been published. | | ⇒ 9002 | [MDEntryMakerSide](/trading/fix/overview/dictionary/custom-tags) | N\* | Custom field indicating the maker side of a trade. Enabled by sending a [Market Data Request ``](/trading/fix/market-data/exchange-bound-messages/market-data-request) with the custom field [EnableMDEntryMakerSide `<9003>`](/trading/fix/overview/dictionary/custom-tags) set to `TRUE`. Appears when [MDEntryType `<269>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) has value `2 = Trade`.

    Valid values:
    `1 = Buy`
    `2 = Sell`

    See [Examples: Showing Maker Side For Trades](/trading/fix/market-data/examples/market-data-responses). | | ⇒ 9009 | [MDEntryFundingIsRealized](/trading/fix/overview/dictionary/custom-tags) | N\* | Custom field used in [Market Data – Incremental Refresh ``](/trading/fix/market-data/client-bound-messages/market-data-incremental-refresh) MDEntry groups when [MDEntryType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_269.html) had the value `S = Funding Amount` to indicate the IsRealized field of Funding Amount.

    Valid values:
    `Y = Yes`
    `N = No` | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | Y | | --- URL: https://developer.gemini.com/trading/fix/drop-copy/workflow/understanding-price-and-quantity.md # Understanding price and quantity In a [Trade Capture Report ``](/trading/fix/drop-copy/client-bound-messages/trade-capture-report), the [LastQty `<32>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_31.html) field is denominated in BTC and the [LastPx `<31>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_31.html) field is denominated in USD. ```txt RAW 8=FIX.4.4|9=236|35=AE|34=17|49=GEMINI|52=20160301-21:38:35.688|56=CLIENT-DC|31=301.42|32=0.02|55=BTCUSD|60=20160301-21:38:35.591|75=20160301|570=N|571=40987|552=1|54=1|37=40979|11=ORD1|453=1|448=CLIENT-OE|447=D|452=11|12=0.120568|13=3|479=USD|58=TAKER|10=085| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 236 34 MsgSeqNum: 17 35 MsgType: TradeCaptureReport (AE) 49 SenderCompID: GEMINI 52 SendingTime: 20160301-21:38:35.688 56 TargetCompID: CLIENT-DC BODY 31 LastPx: 301.42 32 LastQty: 0.02 55 Symbol: BTCUSD 60 TransactTime: 20160301-21:38:35.591 75 TradeDate: 20160301 570 PreviouslyReported: N 571 TradeReportID: 40987 552 NoSides: count = 1 11 ClOrdID: ORD1 12 Commission: 0.120568 13 CommType: ABSOLUTE (3) 37 OrderID: 40979 54 Side: BUY (1) 58 Text: TAKER 479 CommCurrency: USD 453 NoPartyIDs: count = 1 447 PartyIDSource: PROPRIETARY_CUSTOM_CODE (D) 448 PartyID: CLIENT-OE 452 PartyRole: ORDER_ORIGINATION_TRADER (11) TRAILER 10 CheckSum: 085 ``` --- URL: https://developer.gemini.com/trading/fix/drop-copy/workflow/currency-based-messages-and-fields.md # Currency-based messages and fields ### Message and Fields | Message | Fields denominated in price currency | Fields denominated in quantity currency | | ---------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------------------------------------------------------------------------- | | [Trade Capture Report ``](/trading/fix/drop-copy/client-bound-messages/trade-capture-report) | [LastPx `<31>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_31.html) | [LastQty `<32>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_32.html) | --- ### Fees The amount in [Commission `<12>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_12.html) is denominated in the currency specified by the [currency code](https://www.onixs.biz/fix-dictionary/4.4/app_6_a.html) value in [CommCurrency `<479>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_479.html). --- URL: https://developer.gemini.com/trading/fix/drop-copy/workflow/about.md # About Gemini will report all trades on the exchange using [Trade Capture Report ``](/trading/fix/drop-copy/client-bound-messages/trade-capture-report). Trades can occur via orders placed using: - a FIX API session - a REST API session - user logged in to the website Drop Copy reports on trades from all of these sources, distinguished by the value supplied in the [PartyID `<448>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_448.html) field. --- URL: https://developer.gemini.com/trading/fix/drop-copy/party-ids-&-roles/third-party-support.md # Third Party Support When an OMS/OEMS account is set up to place orders on behalf of one or more other accounts, this account will receive trade capture reports for: - all their own trades - all the orders placed by the other accounts - orders placed by the OMS/OEMS on behalf of the other account - orders placed by the other account on Gemini outside the OMS/OEMS, using the UI or any other API When an order is placed, the trade capture report will contain two party IDs with the following [PartyRole `<452>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_452.html) values: --- ### Party Fields | Field | Tag | Value | | ------------------------------------------------------------------------- | --- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | [NoPartyIDs](https://www.onixs.biz/fix-dictionary/4.4/tagNum_453.html) | 453 | `2` | | _First Group_ | | | | [PartyID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_448.html) | 448 | The API session key of the REST or FIX session that placed the order; "UI" when placed using the website. | | [PartyRole](https://www.onixs.biz/fix-dictionary/4.4/tagNum_452.html) | 452 | `11 = Order Origination Trader` | | [PartyIDSource](https://www.onixs.biz/fix-dictionary/4.4/tagNum_447.html) | 447 | `D = Proprietary/Custom Code` | | _Second Group_ | | | | [PartyID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_448.html) | 448 | The [OnBehalfOfCompID `<115>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_115.html) assigned to the account the order was placed on behalf of | | [PartyRole](https://www.onixs.biz/fix-dictionary/4.4/tagNum_452.html) | 452 | `1 = Executing Firm` | | [PartyIDSource](https://www.onixs.biz/fix-dictionary/4.4/tagNum_447.html) | 447 | `D = Proprietary/Custom Code` | --- URL: https://developer.gemini.com/trading/fix/drop-copy/party-ids-&-roles/fields.md # Fields Trade capture reports for trades placed by your own account use one party ID with the following [PartyRole `<452>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_452.html) value: --- ### Party Fields | Field | Tag | Value | | ------------------------------------------------------------------------- | --- | --------------------------------------------------------------------------------------------------------- | | [NoPartyIDs](https://www.onixs.biz/fix-dictionary/4.4/tagNum_453.html) | 453 | `1` | | _First Group_ | | | | [PartyID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_448.html) | 448 | The API session key of the REST or FIX session that placed the order; "UI" when placed using the website. | | [PartyRole](https://www.onixs.biz/fix-dictionary/4.4/tagNum_452.html) | 452 | `11 = Order Origination Trader` | | [PartyIDSource](https://www.onixs.biz/fix-dictionary/4.4/tagNum_447.html) | 447 | `D = Proprietary/Custom Code` | --- URL: https://developer.gemini.com/trading/fix/drop-copy/identifiers/identifiers-assigned-by-gemini.md # Identifiers assigned by Gemini ### Identifiers | Tag | Name | Defined In | Description | | :-: | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 49 | [SenderCompID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_49.html) | [Standard Header](/trading/fix/overview/standard-header) | Assigned value used to identify the message is sent from Gemini to the client, `GEMINI` | | 56 | [TargetCompID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_56.html) | [Standard Header](/trading/fix/overview/standard-header) | Assigned value used to identify the firm receiving the message, e.g. `CLIENT-DC` | | 571 | [TradeReportID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_571.html) | [Trade Capture Report ``](/trading/fix/drop-copy/client-bound-messages/trade-capture-report) | Globally unique event identifier | | 448 | [PartyID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_448.html) | [Trade Capture Report ``](/trading/fix/drop-copy/client-bound-messages/trade-capture-report) | The party that placed the trade.

    - For FIX sessions, the CompID of the sender
    - For API sessions, the session identifier (which is the API key of the sendder)
    - For orders placed on the website, `UI` | --- URL: https://developer.gemini.com/trading/fix/drop-copy/identifiers/client-supplied-identifiers.md # Client supplied identifiers ### Identifiers | Tag | Name | Defined in | Echoed back | Description | | --- | ------------------------------------------------------------------ | ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------- | | 11 | [ClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) | [New Order Single ``](/trading/fix/order-entry/exchange-bound-messages/new-order-single-limit) | [Trade Capture Report ``](/trading/fix/drop-copy/client-bound-messages/trade-capture-request) | The client's request identifier for an order. | --- URL: https://developer.gemini.com/trading/fix/drop-copy/examples/buy-limit-order.md # Buy Limit Order In this example, `TRADEBOTDC002` receives a trade capture report showing that a limit order identified by [ClOrdID `<11>`](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) `KsL0nZY2gLLJSvuY6Z` was filled for 0.001 BTC at 8490.50 USD. ```txt RAW 8=FIX.4.4|9=270|35=AE|34=2|49=GEMINI|52=20180425-18:08:42.445|56=TRADEBOTDC002|31=8490.50|32=0.001|55=BTCUSD|60=20180425-18:08:42.444|75=20180425|570=N|571=335278183|552=1|54=1|37=335278181|11=KsL0nZY2gLLJSvuY6Z|453=1|448=TRADEBOTOE002|447=D|452=11|12=0.008490500|13=3|479=USD|58=TAKER|10=217| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 270 34 MsgSeqNum: 2 35 MsgType: TradeCaptureReport (AE) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-18:08:42.445 56 TargetCompID: TRADEBOTDC002 BODY 31 LastPx: 8490.50 32 LastQty: 0.001 55 Symbol: BTCUSD 60 TransactTime: 20180425-18:08:42.444 75 TradeDate: 20180425 570 PreviouslyReported: N 571 TradeReportID: 335278183 552 NoSides: count = 1 11 ClOrdID: KsL0nZY2gLLJSvuY6Z 12 Commission: 0.008490500 13 CommType: ABSOLUTE (3) 37 OrderID: 335278181 54 Side: BUY (1) 58 Text: TAKER 479 CommCurrency: USD 453 NoPartyIDs: count = 1 447 PartyIDSource: PROPRIETARY_CUSTOM_CODE (D) 448 PartyID: TRADEBOTOE002 452 PartyRole: ORDER_ORIGINATION_TRADER (11) TRAILER 10 CheckSum: 217 ``` --- URL: https://developer.gemini.com/trading/fix/drop-copy/examples/buy-limit-order-third-party.md # Buy Limit Order on Behalf of Third Party In this example, `TRADEBOTDC001`, an OMS, has placed a third-party order on behalf of an account identified by `AA1AA1AAA1`. ```txt RAW 8=FIX.4.4|9=297|35=AE|34=2|49=GEMINI|52=20180425-17:50:34.508|56=TRADEBOTDC001|31=8490.68|32=0.001|55=BTCUSD|60=20180425-17:50:34.506|75=20180425|570=N|571=335278079|552=1|54=1|37=335278077|11=opFrFzIMQksPhSVfvx|453=2|448=TRADEBOTOE001|447=D|452=11|448=AA1AA1AAA1|447=D|452=1|12=0.021226700|13=3|479=USD|58=TAKER|10=028| HEADER 8 BeginString: FIX.4.4 9 BodyLength: 297 34 MsgSeqNum: 2 35 MsgType: TradeCaptureReport (AE) 49 SenderCompID: GEMINI 52 SendingTime: 20180425-17:50:34.508 56 TargetCompID: TRADEBOTDC001 BODY 31 LastPx: 8490.68 32 LastQty: 0.001 55 Symbol: BTCUSD 60 TransactTime: 20180425-17:50:34.506 75 TradeDate: 20180425 570 PreviouslyReported: N 571 TradeReportID: 335278079 552 NoSides: count = 1 11 ClOrdID: opFrFzIMQksPhSVfvx 12 Commission: 0.021226700 13 CommType: ABSOLUTE (3) 37 OrderID: 335278077 54 Side: BUY (1) 58 Text: TAKER 479 CommCurrency: USD 453 NoPartyIDs: count = 2 447 PartyIDSource: PROPRIETARY_CUSTOM_CODE (D) 448 PartyID: TRADEBOTOE001 452 PartyRole: ORDER_ORIGINATION_TRADER (11) ---- 447 PartyIDSource: PROPRIETARY_CUSTOM_CODE (D) 448 PartyID: AA1AA1AAA1 452 PartyRole: EXECUTING_FIRM (1) TRAILER 10 CheckSum: 028 ``` --- URL: https://developer.gemini.com/trading/fix/drop-copy/client-bound-messages/trade-capture-report.md # Trade Capture Report # Gemini will send a [Trade Capture Report ``](https://www.onixs.biz/fix-dictionary/4.4/msgType_AE_6569.html) for all fills against orders placed by the client. This includes orders from all FIX sessions, REST sessions, and orders entered through the UI. See [Party IDs and Roles](/trading/fix/drop-copy/party-ids-&-roles/fields) and [Third Party Support](/trading/fix/drop-copy/party-ids-&-roles/third-party-support) for a detailed explanation of how party IDs and roles are used. --- ### Fields | Tag | Name | Description | | --------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | | [Standard Header](/trading/fix/overview/standard-header) | [MsgType](https://www.onixs.biz/fix-dictionary/4.4/msgs_by_msg_type.html) = `AE` | | 571 | [TradeReportID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_571.html) | The TradeID. This is compatible with the trade IDs returned through the REST API as well as the ExecID returned in the ExecutionReports in the Order FIX channel. | | 570 | [PreviouslyReported](https://www.onixs.biz/fix-dictionary/4.4/tagNum_570.html) | Will always be `N`. Client should use the MsgSeqNum, which will be globally unique. | | 75 | [TradeDate](https://www.onixs.biz/fix-dictionary/4.4/tagNum_75.html) | Required by the FIX spec; will be the date associated with the TransactTime below. | | 60 | [TransactTime](https://www.onixs.biz/fix-dictionary/4.4/tagNum_60.html) | The time that the trade was executed. | | 552 | [NoSides](https://www.onixs.biz/fix-dictionary/4.4/tagNum_552.html) | The number of sides, always `1`. | | => 32 | [LastQty](https://www.onixs.biz/fix-dictionary/4.4/tagNum_32.html) | The quantity executed. | | => 31 | [LastPx](https://www.onixs.biz/fix-dictionary/4.4/tagNum_31.html) | The price of the execution. | | => 11 | [ClOrdID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_11.html) | The client-assigned order ID. Tag will not be sent for UI-based orders. | | => 37 | [OrderID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_37.html) | The Gemini-assigned order ID. | | => 54 | [Side](https://www.onixs.biz/fix-dictionary/4.4/tagNum_54.html) | The side of the order.

    Values:
    `1 = Buy`
    `2 = Sell` | | => 58 | [Text](https://www.onixs.biz/fix-dictionary/4.4/tagNum_58.html) | Used to store the liquidity code. Will be the literal string:
    `MAKER = Added Liquidity`
    `TAKER = Removed Liquidity` | | => 12 | [Commission](https://www.onixs.biz/fix-dictionary/4.4/tagNum_12.html) | Fee charged for trade. Negative for rebates. | | => 13 | [CommType](https://www.onixs.biz/fix-dictionary/4.4/tagNum_13.html) | Type of commission.

    Allowed values:
    `3 = absolute` (total monetary amount) | | => 479 | [CommCurrency](https://www.onixs.biz/fix-dictionary/4.4/tagNum_479.html) | The currency of the fee. | | => 453 | [NoPartyIDs](https://www.onixs.biz/fix-dictionary/4.4/tagNum_453.html) | The number of parties:
    - `1` signifies an order placed by your own account — see [Party IDs and Roles](/trading/fix/drop-copy/party-ids-&-roles/fields)
    - `2` or `3` signifies third-party support — see [Third Party Support](/trading/fix/drop-copy/party-ids-&-roles/third-party-support) | | => => 448 | [PartyID](https://www.onixs.biz/fix-dictionary/4.4/tagNum_448.html) | This will be the CompID of the session that placed the trade. For UI-based orders, this will be the string `UI`. For REST API orders, this will be the session identifier. | | => => 447 | [PartyIDSource](https://www.onixs.biz/fix-dictionary/4.4/tagNum_447.html) | The source of the party ID.

    Values:
    `D = Proprietary/Custom Code` | | => => 452 | [PartyRole](https://www.onixs.biz/fix-dictionary/4.4/tagNum_452.html) | The role of the party.

    Values:
    `11 = Order Origination Trader`
    `1 = Executing Firm` (used for Third Party Support)
    `24 = Customer Account` (used for Third Party Support)
    `16 = Executing System` (used for Third Party Support) | | | [Standard Trailer](/trading/fix/overview/standard-trailer) | | --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/trading/order-lifecycle.md # TypeScript SDK — Trading: Order Lifecycle Methods for creating, cancelling, and inspecting orders, plus the session heartbeat and wrapped-order flow. All methods are on `client.trading`. Every method in this namespace is a **POST mutation** — the SDK will **not** automatically retry on failure. If you need retry logic for idempotent operations, implement it in your application code. See the [API Specifications](/api-specifications) for full request/response schemas and the [Error Handling guide](/tools/typescript-sdk/errors) for error types. ### createNewOrder `POST /v1/order/new` · Authenticated Place a new order on the exchange. Supports limit orders across all spot trading pairs. The SDK validator also accepts `"exchange market"` as a type, but market order support is limited — not all symbols or account configurations support market orders. Prefer limit orders for reliable execution. ```ts const order = await client.trading.createNewOrder({ symbol: "BTCUSD", amount: "0.01", price: "50000.00", side: "buy", type: "exchange limit", }); console.log(order.order_id); // unique order identifier ``` > **Validated client-side.** The SDK validates the request body before sending. If validation fails, a `ValidationError` is thrown and no network request is made. See [Request Validation](/tools/typescript-sdk/deep-dives/request-validation). > **Tip:** Prices and amounts are **decimal strings**, never numbers. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### cancelOrder `POST /v1/order/cancel` · Authenticated Cancel a single active order by its order ID. ```ts const result = await client.trading.cancelOrder({ order_id: 12345678n, }); console.log(result.is_cancelled); // true if the order was successfully cancelled ``` > **Validated client-side.** The request is validated locally before sending. > **Caveat:** The `order_id` field accepts `bigint` or `number`. If you received the ID from another API call, it may already be a `bigint`. ### cancelAllActiveOrders `POST /v1/order/cancel/all` · Authenticated Cancel every active order on the account across all trading pairs and sessions. ```ts const result = await client.trading.cancelAllActiveOrders({}); console.log(result.result); // "ok" on success console.log(result.details); // { cancelledOrders: [...], cancelRejects: [...] } ``` > **Validated client-side.** The request is validated locally before sending. > **Caution:** This cancels orders placed by **all** sessions and API keys on the account, not just the current session. ### cancelAllSessionOrders `POST /v1/order/cancel/session` · Authenticated Cancel all active orders placed by the current session only. Orders placed by other API keys or sessions are unaffected. ```ts const result = await client.trading.cancelAllSessionOrders({}); console.log(result.result); // "ok" on success console.log(result.details); // { cancelledOrders: [...], cancelRejects: [...] } ``` > **Validated client-side.** The request is validated locally before sending. > **Tip:** Pair this with `sendHeartbeat` to implement a dead-man's switch — if heartbeats stop, session orders are automatically cancelled by the exchange. You must first enable **Require Heartbeat** on your API key in the Gemini dashboard. ### getOrderStatus `POST /v1/order/status` · Authenticated Retrieve the current status of a single order. ```ts const status = await client.trading.getOrderStatus({ order_id: 12345678n, }); console.log(status.symbol); // "BTCUSD" console.log(status.side); // "buy" console.log(status.original_amount); // "0.01" console.log(status.executed_amount); // "0.005" — filled so far console.log(status.is_live); // true if still on the book console.log(status.is_cancelled); // true if cancelled ``` > **Validated client-side.** The request is validated locally before sending. ### wrapOrder `POST /v1/wrap/{symbol}` · Authenticated Place a wrapped order. All path parameters (`symbol`) and body fields (`amount`, `side`) are passed in a single flat parameter object. ```ts const result = await client.trading.wrapOrder({ symbol: "BTCGUSD", amount: "1.0", side: "buy", }); console.log(result.orderId); ``` > **Validated client-side.** The request is validated locally before sending. ### sendHeartbeat `POST /v1/heartbeat` · Authenticated Send a session heartbeat to the exchange. When heartbeating is active, the exchange will automatically cancel all session orders if it stops receiving heartbeats within the timeout window. ```ts const result = await client.trading.sendHeartbeat(); console.log(result.result); // "ok" ``` > **Tip:** Instead of calling this manually, use `client.createHeartbeat()` which returns a `ManagedHeartbeat` that sends heartbeats on a configurable interval and handles errors. You must first enable **Require Heartbeat** on your API key in the Gemini dashboard. > > ```ts > const heartbeat = client.createHeartbeat({ > intervalMs: 15_000, > onError: (err) => console.error("Heartbeat failed:", err), > }); > heartbeat.start(); > > // Later, when shutting down: > heartbeat.stop(); > ``` ## What's next - [Trading: History & Volume](/tools/typescript-sdk/reference/trading/history-and-volume) — query active/past orders, trades, and volume - [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — real-time order updates via `client.websocket.private.orders({ scope: "session" })` - [Error Handling](/tools/typescript-sdk/errors) — error types and metadata - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/trading/history-and-volume.md # TypeScript SDK — Trading: History & Volume Methods for querying active orders, historical orders, past trades, and trading-volume statistics. All methods are on `client.trading`. Every method in this namespace is a **POST mutation** — the SDK will **not** automatically retry on failure. See the [API Specifications](/api-specifications) for full request/response schemas. ### listActiveOrders `POST /v1/orders` · Authenticated Retrieve all currently active (live) orders on the account. ```ts const orders = await client.trading.listActiveOrders({}); for (const order of orders) { console.log(order.order_id, order.symbol, order.side, order.price); } ``` > **Tip:** For real-time order updates without polling, subscribe to `client.websocket.private.orders({ scope: "account" })` instead. See the [WebSocket Reference](/tools/typescript-sdk/reference/websocket). ### listPastOrders `POST /v1/orders/history` · Authenticated Retrieve historical orders. You can filter by timestamp to paginate through results. ```ts const orders = await client.trading.listPastOrders({ symbol: "BTCUSD", timestamp: 1625000000000n, // only orders after this time limit_orders: 50, }); for (const order of orders) { console.log(order.order_id, order.type, order.executed_amount); } ``` > **Caveat:** The `timestamp` field is a millisecond epoch value and may be `bigint`. The SDK accepts both `bigint` and `number` for int64 input fields. ### listPastTrades `POST /v1/mytrades` · Authenticated Retrieve your executed trades (fills). Each entry represents one side of a matched trade. ```ts const trades = await client.trading.listPastTrades({ symbol: "BTCUSD", timestamp: 1625000000000n, limit_trades: 100, }); for (const trade of trades) { console.log(trade.tid); // trade ID (bigint) console.log(trade.price); // execution price (decimal string) console.log(trade.amount); // fill amount (decimal string) console.log(trade.fee_amount); // fee charged (decimal string) console.log(trade.fee_currency); // fee currency } ``` > **Caveat:** The `tid` (trade ID) field in the response is a `bigint`. The `timestamp` input accepts both `bigint` and `number`. > **Tip:** Prices, amounts, and fees are **decimal strings**. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### getTradingVolume `POST /v1/tradevolume` · Authenticated Retrieve your 30-day trading volume, broken down by fee tier and trading pair. ```ts const volume = await client.trading.getTradingVolume({}); for (const entry of volume) { console.log(entry.symbol); // e.g. "btcusd" console.log(entry.base_currency); // e.g. "BTC" console.log(entry.total_volume_base); // total volume in base currency (decimal string) console.log(entry.buy_maker_notional); // buy maker notional volume (decimal string) console.log(entry.sell_maker_notional);// sell maker notional volume (decimal string) console.log(entry.buy_maker_count); // number of buy maker trades } ``` > **Tip:** Volume values are **decimal strings**. Count values (`buy_maker_count`, etc.) are **numbers**. ### getNotionalTradingVolume `POST /v1/notionalvolume` · Authenticated Retrieve your 30-day notional trading volume in USD. This is used to determine your fee tier. ```ts const result = await client.trading.getNotionalTradingVolume({}); console.log(result.notional_30d_volume); // 30-day USD volume (decimal string) console.log(result.api_maker_fee_bps); // current maker fee in basis points (number) console.log(result.api_taker_fee_bps); // current taker fee in basis points (number) ``` > **Tip:** The `notional_30d_volume` field is a **decimal string**, while `api_maker_fee_bps` and `api_taker_fee_bps` are **numbers** (basis points). ## What's next - [Trading: Order Lifecycle](/tools/typescript-sdk/reference/trading/order-lifecycle) — place, cancel, and inspect orders - [WebSocket Reference](/tools/typescript-sdk/reference/websocket) — real-time order and trade streams - [Patterns](/tools/typescript-sdk/patterns) — pagination and common workflows - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and bigint handling --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/predictions/volume-and-metrics.md # TypeScript SDK — Predictions: Volume & Metrics Query historical volume data and trading metrics for prediction markets. All methods are on `client.predictions`. See the [API specifications](/api-specifications) for full response schemas. ### getPredictionMarketDailyVolume `GET /v1/prediction-markets/volume/{date}` · Public Get the aggregate daily trading volume for prediction markets on a specific date. Returns a bare array of `PredictionMarketVolumeCategory` objects (not wrapped in an object). ```ts const categories = await client.predictions.getPredictionMarketDailyVolume({ date: "2026-06-15", }); for (const cat of categories) { console.log(cat.categoryPath, cat.volume); // volume is a decimal string } ``` > **Auto-retry** — This GET endpoint automatically retries on 429/502/503/504 status codes. ### getPredictionMarketHourlyVolume `GET /v1/prediction-markets/volume/{date}/hourly` · Public Get hourly volume breakdowns for prediction markets on a specific date. Returns a bare array of `PredictionMarketHourlyVolumeCategory` objects. ```ts const hourly = await client.predictions.getPredictionMarketHourlyVolume({ date: "2026-06-15", }); for (const entry of hourly) { console.log(entry.periodStart, entry.categoryPath, entry.volume); } ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### getVolumeMetrics `POST /v1/prediction-markets/metrics/volume` · Authenticated Get per-contract share volume metrics for an event. This is a POST mutation — never automatically retried. The `eventTicker` field is **required**. Returns a `VolumeMetricsResponse` with `eventTicker` and a `contracts` array of per-contract volume data. ```ts const metrics = await client.predictions.getVolumeMetrics({ eventTicker: "FED260318", startTime: 1700000000000n, endTime: 1700086400000n, }); console.log(metrics.eventTicker); for (const contract of metrics.contracts ?? []) { console.log(contract.symbol, contract.totalQty); console.log(" user aggressor:", contract.userAggressorQty); console.log(" user resting:", contract.userRestingQty); } ``` > **Tip** — `startTime` and `endTime` accept both `bigint` and `number` values. Both are optional — omit to get all-time volume. See [Data Types](/tools/typescript-sdk/deep-dives/data-types) for int64 handling. ## What's next - [Events & Discovery](/tools/typescript-sdk/reference/predictions/events-and-discovery) — browse and search prediction market events - [Order Management](/tools/typescript-sdk/reference/predictions/order-management) — place, cancel, and query orders - [Rewards & Rebates](/tools/typescript-sdk/reference/predictions/rewards-and-rebates) — liquidity rewards and maker rebate programs - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — why prices are strings and timestamps may be `bigint` --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/predictions/rewards-and-rebates.md # TypeScript SDK — Predictions: Rewards & Rebates Query liquidity rewards configuration, summaries, and maker rebate details. All methods are on `client.predictions`. See the [API specifications](/api-specifications) for full response schemas. ## Liquidity Rewards ### getLiquidityRewardsConfig `GET /v1/prediction-markets/liquidity-rewards/config` · Public Get the current liquidity rewards program configuration. Returns a `LiquidityRewardsConfig` object. ```ts const config = await client.predictions.getLiquidityRewardsConfig(); console.log(config.enabled); // boolean console.log(config.max_spread_cents); // number (only when enabled) console.log(config.min_payout_threshold_usd); // decimal string (only when enabled) ``` > **Auto-retry** — This GET endpoint automatically retries on 429/502/503/504 status codes. ### getLiquidityRewardsDailySummary `GET /v1/prediction-markets/liquidity-rewards/summary/daily` · Authenticated Get a daily breakdown of your liquidity rewards over a date range. Both `dateFrom` and `dateTo` are **required**. Returns a `LiquidityRewardsDailySummaryResponse` with a `.daily_summaries` array. ```ts const daily = await client.predictions.getLiquidityRewardsDailySummary({ dateFrom: "2026-06-01", dateTo: "2026-06-30", }); for (const day of daily.daily_summaries) { console.log(day.payout_date, day.total_reward_usd, day.payout_status); for (const ev of day.events) { console.log(" ", ev.event_name, ev.event_reward_usd, ev.normalized_score); } } ``` > **Tip** — Response fields at `daily_summaries[*].events[*].event_id` are `bigint`. > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### getLiquidityRewardsLifetimeSummary `GET /v1/prediction-markets/liquidity-rewards/summary/total` · Authenticated Get your lifetime aggregate liquidity rewards. Optionally filter by date range. ```ts // Lifetime total const lifetime = await client.predictions.getLiquidityRewardsLifetimeSummary(); console.log(lifetime.total_earned_usd); console.log(lifetime.payout_count); console.log(lifetime.first_payout_date); // "YYYY-MM-DD" or null console.log(lifetime.last_payout_date); // "YYYY-MM-DD" or null // Scoped to a date range const scoped = await client.predictions.getLiquidityRewardsLifetimeSummary({ dateFrom: "2026-01-01", dateTo: "2026-06-30", }); ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### listLiquidityRewardsEvents `GET /v1/prediction-markets/liquidity-rewards/events` · Public List events that participate in the liquidity rewards program, with optional filters. Returns a `LiquidityRewardsEventsResponse` with an `.events` array, `.pagination`, and `.last_score_date`. ```ts const result = await client.predictions.listLiquidityRewardsEvents({ category: "Crypto", search: "bitcoin", sort: "daily_pool_desc", limit: 20, offset: 0, }); for (const ev of result.events) { console.log(ev.event_ticker, ev.title, ev.daily_pool_usd, ev.category); } console.log(result.pagination.total); console.log(result.last_score_date); ``` **Sort options:** `"daily_pool_desc"`, `"daily_pool_asc"`, `"ends_soonest"`, `"ends_latest"`, `"title_asc"`, `"title_desc"`, `"category_asc"`, `"category_desc"`, `"competition_asc"`, `"competition_desc"`. > **Auto-retry** — This GET endpoint automatically retries on transient failures. ## Maker Rebates ### getMakerRebateRates `GET /v1/prediction-markets/maker-rebate/rates` · Public Get the current maker rebate rate schedule, optionally filtered by category. Returns a `MakerRebateRatesResponse` with a `.rate_rules` array. ```ts // All rates const rates = await client.predictions.getMakerRebateRates(); // Filtered by category const cryptoRates = await client.predictions.getMakerRebateRates({ category: "Crypto", }); for (const rule of cryptoRates.rate_rules) { console.log(rule.id, rule.rebate_multiplier_bps, rule.effective_from, rule.category); } ``` > **Tip** — `rate_rules[*].id` values are `bigint`. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### getMakerRebateLifetimeSummary `GET /v1/prediction-markets/maker-rebate/summary/total` · Authenticated Get your lifetime maker rebate summary, optionally scoped to a date range. ```ts // Lifetime total const summary = await client.predictions.getMakerRebateLifetimeSummary(); console.log(summary.total_earned_usd); console.log(summary.total_fill_count); // bigint console.log(summary.total_volume_usd); console.log(summary.payout_count); console.log(summary.first_payout_date); // "YYYY-MM-DD" or null console.log(summary.last_payout_date); // "YYYY-MM-DD" or null // Scoped const scoped = await client.predictions.getMakerRebateLifetimeSummary({ dateFrom: "2026-01-01", dateTo: "2026-06-30", }); ``` > **Tip** — `total_fill_count` is `bigint`. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### listMakerRebatePayouts `POST /v1/prediction-markets/maker-rebate/payouts` · Authenticated List your maker rebate payout history. Despite being a POST endpoint, parameters are passed as **query params**. Returns a `MakerRebatePayoutsResponse` with a `.payouts` array. ```ts const result = await client.predictions.listMakerRebatePayouts({ limit: 50, offset: 0, }); for (const payout of result.payouts) { console.log(payout.id, payout.total_rebate_usd, payout.status, payout.paid_at); } ``` > **Tip** — `payouts[*].id` values are `bigint`. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). > **Note** — POST endpoints are never automatically retried by the SDK. ## What's next - [Events & Discovery](/tools/typescript-sdk/reference/predictions/events-and-discovery) — browse and search prediction market events - [Order Management](/tools/typescript-sdk/reference/predictions/order-management) — place, cancel, and query orders - [Volume & Metrics](/tools/typescript-sdk/reference/predictions/volume-and-metrics) — daily/hourly volume and per-event share metrics - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — why `total_fill_count` and `payout.id` are `bigint` --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/predictions/positions-and-terms.md # TypeScript SDK — Predictions: Positions & Terms Query open and settled positions, and manage prediction markets terms acceptance. All methods are on `client.predictions`. See the [patterns guide](/tools/typescript-sdk/patterns) for the recommended terms-acceptance flow. ### getPositions `POST /v1/prediction-markets/positions` · Authenticated Retrieve your current open prediction market positions. Despite being a POST endpoint, parameters are passed as **query params**. Returns a `PositionsResponse` with a `.positions` array and `.total` count. ```ts // All positions const result = await client.predictions.getPositions(); // Filter by event with pagination and sort const filtered = await client.predictions.getPositions({ eventTicker: "FEDJAN26", limit: 50, offset: 0, sort: "-positionValue", }); for (const pos of filtered.positions ?? []) { console.log(pos.symbol, pos.totalQuantity, pos.avgPrice, pos.outcome); } console.log(filtered.total); // total position count ``` **Sort options:** `"positionValue"`, `"+positionValue"`, `"-positionValue"`, `"unrealizedPnl"`, `"+unrealizedPnl"`, `"-unrealizedPnl"`, `"expiryDate"`, `"+expiryDate"`, `"-expiryDate"`. Prefix `+` for ascending, `-` for descending. > **Note** — Although this uses POST on the wire, the SDK passes `eventTicker`, `limit`, `offset`, and `sort` as query parameters, not a request body. POST endpoints are never automatically retried by the SDK. ### getSettledPositions `POST /v1/prediction-markets/positions/settled` · Authenticated Retrieve your settled (resolved) prediction market positions. Despite being a POST endpoint, parameters are **query params**. Returns a `SettledPositionsResponse` with a `.positions` array and `.total` count. ```ts const settled = await client.predictions.getSettledPositions({ eventTicker: "FEDJAN26", limit: 20, sort: "-date", withCashOuts: true, }); for (const pos of settled.positions ?? []) { console.log(pos.instrumentSymbol, pos.payout, pos.resolutionSide, pos.netProfit); } // Cash-outs (only present when withCashOuts: true) if (settled.cashOuts) { for (const co of settled.cashOuts) { console.log(co.instrumentSymbol, co.proceeds, co.netProfit); } } ``` **Sort options:** `"date"`, `"-date"`, `"payout"`, `"+payout"`, `"-payout"`. A bare field name defaults to descending. > **Note** — Although this uses POST on the wire, the SDK passes parameters as query parameters. POST endpoints are never automatically retried by the SDK. ### acceptTerms `POST /v1/prediction-markets/terms/accept` · Authenticated Accept the latest prediction markets terms of service. This is a POST mutation — never automatically retried. You must call this before placing orders if you haven't already accepted. ```ts const result = await client.predictions.acceptTerms(); console.log(result.success); // true ``` ### getPredictionMarketsTerms `GET /v1/prediction-markets/terms` · Public Retrieve the current prediction markets terms of service. Returns a `PredictionMarketsTerms` object with `termsType`, `version`, `content`, and `updatedAt` fields. ```ts const terms = await client.predictions.getPredictionMarketsTerms(); console.log(terms.version); // e.g. 3 console.log(terms.content); // terms text console.log(terms.updatedAt); // ISO 8601 timestamp ``` > **Auto-retry** — This GET endpoint automatically retries on 429/502/503/504 status codes. ### getPredictionMarketsTermsStatus `GET /v1/prediction-markets/terms/status` · Authenticated Check whether the authenticated account has accepted the latest terms. Returns a `PredictionMarketsTermsStatus` object for callers that want a proactive UI check. Order endpoints remain authoritative and return `AcceptTermsRequired` when terms are needed. ```ts const status = await client.predictions.getPredictionMarketsTermsStatus(); console.log(status.hasAcceptedLatest); // boolean console.log(status.acceptedVersion); // number | null console.log(status.latestVersion); // number | null if (!status.hasAcceptedLatest) { await client.predictions.acceptTerms(); } ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. > **Tip** — Use this endpoint when the UI needs to show terms state before an order attempt. Otherwise, handle `AcceptTermsRequired` from the order endpoint. See the [patterns guide](/tools/typescript-sdk/patterns) for the recommended flow. ## What's next - [Events & Discovery](/tools/typescript-sdk/reference/predictions/events-and-discovery) — browse and search prediction market events - [Order Management](/tools/typescript-sdk/reference/predictions/order-management) — place, cancel, and query orders - [Combos](/tools/typescript-sdk/reference/predictions/combos) — multi-leg combo instruments - [Volume & Metrics](/tools/typescript-sdk/reference/predictions/volume-and-metrics) — daily/hourly volume and per-event share metrics - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/predictions/order-management.md # TypeScript SDK — Predictions: Order Management Place, cancel, and query prediction market orders. All methods are on `client.predictions`. Every order method requires authentication. See the [errors guide](/tools/typescript-sdk/errors) for error handling patterns. ### placeOrder `POST /v1/prediction-markets/order` · Authenticated Place a single prediction market order. This is a POST mutation — never automatically retried. The request body is validated locally before the endpoint is called. Terms acceptance is a server-side business rule: when the latest terms have not been accepted, the endpoint returns `AcceptTermsRequired`. Invalid fields throw a `ValidationError` without making a network call. ```ts import { AcceptTermsRequired, ValidationError } from "@gemini-markets/sdk/server"; try { const order = await client.predictions.placeOrder({ symbol: "GEMI-FEDJAN26-DN25", orderType: "limit", side: "buy", quantity: "10", price: "0.65", outcome: "yes", timeInForce: "good-til-cancel", makerOrCancel: false, }); console.log(order.orderId); // bigint console.log(order.status); // "open" | "filled" | "cancelled" console.log(order.symbol); } catch (err) { if (err instanceof AcceptTermsRequired) { // Accept terms first, then retry await client.predictions.acceptTerms(); // Retry the order... } if (err instanceof ValidationError) { console.log(err.field, err.rule, err.message); } } ``` **Validated fields:** | Field | Rule | Required | | --- | --- | --- | | `symbol` | string | Yes | | `orderType` | `"limit"` or `"stop-limit"` | Yes | | `side` | `"buy"` or `"sell"` | Yes | | `quantity` | decimal string | Yes | | `price` | decimal string, 0–1 | Yes | | `outcome` | `"yes"` or `"no"` | Yes | | `stopPrice` | decimal string, 0–1 | Required if `orderType` is `"stop-limit"` | | `timeInForce` | `"good-til-cancel"`, `"immediate-or-cancel"`, `"fill-or-kill"` | No | | `makerOrCancel` | boolean | Yes | > **Caveat** — `price` and `quantity` must be decimal strings, not numbers. `price` and `stopPrice` must be between 0 and 1 inclusive. See the [patterns guide](/tools/typescript-sdk/patterns) for the recommended terms-acceptance flow. ### placeOrderBatch `POST /v1/prediction-markets/order/batch` · Authenticated Place up to 20 orders in a single request. This is a POST mutation — never automatically retried. Like `placeOrder`, this method validates each order client-side before sending. If terms are required, the endpoint returns `AcceptTermsRequired`. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). ```ts const result = await client.predictions.placeOrderBatch({ orders: [ { symbol: "GEMI-FEDJAN26-DN25", orderType: "limit", side: "buy", quantity: "5", price: "0.60", outcome: "yes", makerOrCancel: false, }, { symbol: "GEMI-FEDJAN26-DN25", orderType: "limit", side: "buy", quantity: "5", price: "0.35", outcome: "no", makerOrCancel: false, }, ], }); for (const entry of result.results) { if ("order" in entry) { console.log(entry.order.orderId, entry.order.status); } else { console.log(entry.error, entry.message); } } ``` > **Validated** — The `orders` array must contain 1–20 items. Each order is validated with the same rules as `placeOrder`. ### cancelOrder `POST /v1/prediction-markets/order/cancel` · Authenticated Cancel a single active order by its ID. This is a POST mutation — never automatically retried. The `orderId` is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation) as a non-negative integer identifier (number, bigint, or numeric string). ```ts const result = await client.predictions.cancelOrder({ orderId: 12345678n, }); console.log(result.result); // "ok" console.log(result.message); // "Order 12345678 cancelled successfully" ``` ### cancelOrderBatch `POST /v1/prediction-markets/order/batch/cancel` · Authenticated Cancel up to 20 orders in a single request. This is a POST mutation — never automatically retried. Each order ID is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). ```ts const result = await client.predictions.cancelOrderBatch({ orderIds: [12345678n, 98765432n], }); for (const entry of result.results) { if ("result" in entry && entry.result === "ok") { console.log(entry.orderId, "cancelled"); } else if ("error" in entry) { console.log(entry.orderId, entry.error, entry.message); } } ``` > **Validated** — `orderIds` must contain 1–20 non-negative order identifiers. > **Tip** — The response `orderId` fields are returned as `bigint`. See [Data Types](/tools/typescript-sdk/deep-dives/data-types) for int64 handling. ### getActiveOrders `POST /v1/prediction-markets/orders/active` · Authenticated Retrieve your currently active (open) prediction market orders. This is a POST mutation — never automatically retried. The body is optional — omit it to get all active orders. Returns an `OrdersResponse` with an `.orders` array. ```ts // All active orders const active = await client.predictions.getActiveOrders(); // Filter by contract symbol const filtered = await client.predictions.getActiveOrders({ symbol: "GEMI-FEDJAN26-DN25", limit: 50, }); for (const order of active.orders ?? []) { console.log(order.orderId, order.side, order.price, order.outcome); } ``` > **Tip** — The filter field is `symbol` (contract instrument symbol), not `eventTicker`. Response `orderId` values are `bigint`. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### getOrderHistory `POST /v1/prediction-markets/orders/history` · Authenticated Retrieve your historical prediction market orders. This is a POST mutation — never automatically retried. The body is optional — omit it for the default history window. Returns an `OrdersResponse` with an `.orders` array. ```ts // Recent order history const history = await client.predictions.getOrderHistory(); // Filter by status and time range (timestamps are bigint-compatible) const bounded = await client.predictions.getOrderHistory({ status: "filled", symbol: "GEMI-FEDJAN26-DN25", from: 1700000000000n, to: 1700086400000n, }); for (const order of history.orders ?? []) { console.log(order.orderId, order.status, order.side, order.outcome); } ``` > **Tip** — The `from` and `to` fields accept `bigint` timestamps (epoch milliseconds). Response `orderId` values are `bigint`. ## What's next - [Events & Discovery](/tools/typescript-sdk/reference/predictions/events-and-discovery) — browse and search prediction market events - [Positions & Terms](/tools/typescript-sdk/reference/predictions/positions-and-terms) — open and settled positions, terms acceptance - [Combos](/tools/typescript-sdk/reference/predictions/combos) — multi-leg combo instruments - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works - [Error Handling](/tools/typescript-sdk/errors) — error types and metadata --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/predictions/events-and-discovery.md # TypeScript SDK — Predictions: Events & Discovery Browse, search, and inspect prediction market events. All methods are on `client.predictions`. Every method in this section is **public** — no authentication required. See the [API specifications](/api-specifications) for full request and response schemas. ### listEvents `GET /v1/prediction-markets/events` · Public List prediction market events with optional filters for status, category, sport, and more. Returns an `EventsResponse` with a `.data` array of events and a `.pagination` object. ```ts // All active events const response = await client.predictions.listEvents({ status: ["active"] }); for (const event of response.data ?? []) { console.log(event.ticker, event.title, event.status); } // Filter by category and sport const sports = await client.predictions.listEvents({ status: ["active"], category: ["sports"], sport: ["basketball"], limit: 25, }); console.log(sports.pagination?.total); // total matching events ``` > **Auto-retry** — This GET endpoint automatically retries on 429/502/503/504 status codes. ### getEvent `GET /v1/prediction-markets/events/{eventTicker}` · Public Retrieve full details for a single event by its ticker. Returns an `Event` object directly (not wrapped). ```ts const event = await client.predictions.getEvent({ eventTicker: "BTC100K2028" }); console.log(event.title); console.log(event.status); console.log(event.contracts); // tradeable contracts within this event ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### getEventStrike `GET /v1/prediction-markets/events/{eventTicker}/strike` · Public Get the strike (threshold/target) details for an event. Returns a `Strike` object with `value`, `type`, and `availableAt` fields. ```ts const strike = await client.predictions.getEventStrike({ eventTicker: "BTC05M2603271950", }); console.log(strike.value); // e.g. "87500.00" console.log(strike.type); // e.g. "spread", "above", "reference" console.log(strike.availableAt); // ISO 8601 timestamp ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### getCategories `GET /v1/prediction-markets/categories` · Public List available event categories, optionally filtered by status. Returns an object with a `categories` string array. ```ts // All categories const result = await client.predictions.getCategories(); console.log(result.categories); // ["sports", "politics", "crypto", ...] // Only categories with active events const active = await client.predictions.getCategories({ status: ["active"] }); ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### listUpcomingEvents `GET /v1/prediction-markets/events/upcoming` · Public List events that haven't started yet. Returns an `EventsResponse` with a `.data` array, same shape as `listEvents`. ```ts const response = await client.predictions.listUpcomingEvents({ category: ["crypto"], limit: 10, }); for (const event of response.data ?? []) { console.log(event.ticker, event.title, event.effectiveDate); } ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### listNewlyListedEvents `GET /v1/prediction-markets/events/newly-listed` · Public List recently added events. Returns an `EventsResponse` with a `.data` array. Useful for discovery feeds and alerts. ```ts const response = await client.predictions.listNewlyListedEvents({ category: ["sports"], limit: 20, }); for (const event of response.data ?? []) { console.log(event.ticker, event.title, event.createdAt); } ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. ### listRecentlySettledEvents `GET /v1/prediction-markets/events/recently-settled` · Public List events that have recently settled (resolved to an outcome). Returns an `EventsResponse` with a `.data` array. Useful for displaying results. ```ts const response = await client.predictions.listRecentlySettledEvents({ category: ["crypto"], limit: 15, }); for (const event of response.data ?? []) { console.log(event.title, event.status, event.resolvedAt); } ``` > **Auto-retry** — This GET endpoint automatically retries on transient failures. ## What's next - [Order Management](/tools/typescript-sdk/reference/predictions/order-management) — place, cancel, and query prediction market orders - [Positions & Terms](/tools/typescript-sdk/reference/predictions/positions-and-terms) — open and settled positions, terms acceptance - [Combos](/tools/typescript-sdk/reference/predictions/combos) — multi-leg combo instruments - [Volume & Metrics](/tools/typescript-sdk/reference/predictions/volume-and-metrics) — daily/hourly volume and per-event share metrics - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — why prices are strings and timestamps may be `bigint` --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/predictions/combos.md # TypeScript SDK — Predictions: Combos Create and query combo instruments — bundles of prediction market contracts traded as a single unit. All methods are on `client.predictions`. > **Availability** — Combo endpoints are not currently enabled in production. They are available in the sandbox environment for testing. ### createCombo `POST /v1/prediction-markets/combos` · Authenticated Create a new combo instrument from 2–6 contract legs. Each leg specifies a `contractId` (decimal string of the numeric contract ID) and a `requiredOutcome` (`"Yes"` or `"No"` — capitalized). This is a POST mutation — never automatically retried. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). ```ts const result = await client.predictions.createCombo({ legs: [ { contractId: "101", requiredOutcome: "Yes" }, { contractId: "202", requiredOutcome: "No" }, ], }); console.log(result.combo.id); // bigint — internal combo ID console.log(result.combo.instrumentSymbol); // e.g. "GEMI-CMB-0526-A7F3B2C1D4E5" console.log(result.combo.canonicalLegKey); // e.g. "101:Yes|202:No" console.log(result.alreadyExisted); // true if canonical combo already existed ``` **Validated fields per leg:** | Field | Rule | Required | | --- | --- | --- | | `contractId` | string only — the SDK validates it as a string, not a numeric value (passing a number will fail validation) | Yes | | `requiredOutcome` | `"Yes"` or `"No"` (capitalized) | Yes | > **Validated** — The `legs` array must contain 2–6 items. The SDK validates each leg locally and throws `ValidationError` on mismatch before making a network call. See [Request Validation](/tools/typescript-sdk/deep-dives/request-validation). > **Tip** — Response fields `combo.id`, `combo.instrumentId`, and `combo.legs[*].comboId` are `bigint`. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### getComboByInstrumentSymbol `GET /v1/prediction-markets/combos/{instrumentSymbol}` · Public Look up a combo by its instrument symbol. Returns a `ComboResponse` with a `contract` metadata object and a `legs` array. ```ts const combo = await client.predictions.getComboByInstrumentSymbol({ instrumentSymbol: "GEMI-CMB-0526-A7F3B2C1D4E5", }); console.log(combo.contract.contractTicker); console.log(combo.contract.contractStatus); for (const leg of combo.legs) { console.log(leg.contractId, leg.requiredOutcome, leg.legOutcome); } ``` > **Tip** — `legs[*].comboId` values are `bigint`. > **Auto-retry** — This GET endpoint automatically retries on 429/502/503/504 status codes. ### listCombos `GET /v1/prediction-markets/combos` · Public List available combos with optional filters. Returns a `ListCombosResponse` with a `.combos` array and a `.pagination` object. ```ts // All active combos (default status) const result = await client.predictions.listCombos(); // Filter by status (capitalized: "Active", "Settled", "Voided") const filtered = await client.predictions.listCombos({ status: "Active", limit: 25, offset: 0, }); // Filter by underlying contract ID (numeric bigint) const byContract = await client.predictions.listCombos({ contractId: 101n, instrumentRegistered: true, }); for (const combo of filtered.combos) { console.log(combo.contract.contractTicker, combo.legs.length); } ``` > **Tip** — The `status` filter value is capitalized (e.g. `"Active"`, not `"active"`). The `contractId` filter is a numeric ID (`bigint`), not a ticker string. > **Auto-retry** — This GET endpoint automatically retries on transient failures. ## What's next - [Events & Discovery](/tools/typescript-sdk/reference/predictions/events-and-discovery) — browse and search prediction market events - [Order Management](/tools/typescript-sdk/reference/predictions/order-management) — place, cancel, and query orders - [Positions & Terms](/tools/typescript-sdk/reference/predictions/positions-and-terms) — open and settled positions, terms acceptance - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — why `combo.id` and `contractId` are `bigint` - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/market-data/symbols-and-pricing.md # TypeScript SDK — Market Data: Symbols & Pricing Symbol discovery and real-time pricing endpoints. All methods are on `client.marketData`. All methods in this group are public (no authentication required) and backed by `GET` requests, so they auto-retry on transient failures (429, 502, 503, 504). All prices and quantities are **decimal strings**, not floating-point numbers. See [Data Types](/tools/typescript-sdk/deep-dives/data-types) for why this matters and how to handle arithmetic safely. ### listSymbols `GET /v1/symbols` · Public Returns an array of all available trading pair symbols as lowercase strings (e.g. `"btcusd"`, `"ethusd"`). Use this to discover what you can pass to other market data methods. ```ts const symbols = await client.marketData.listSymbols(); // ["aaveusd", "btcusd", "ethusd", ...] ``` > **Tip:** The list can be large (200+ symbols). Consider caching the result and refreshing periodically rather than calling on every request. ### getSymbolDetails `GET /v1/symbols/details/{symbol}` · Public Returns detailed trading rules for a symbol: minimum order size, tick size, quote increment, and more. ```ts const details = await client.marketData.getSymbolDetails({ symbol: "BTCUSD" }); console.log(details.min_order_size); // e.g. "0.00001" (string) console.log(details.tick_size); // e.g. 1e-8 (number) console.log(details.quote_increment); // e.g. 0.01 (number) ``` > **Tip:** Use these values to validate order parameters before submitting. The `min_order_size` field is a decimal string, while `tick_size` and `quote_increment` are numbers. ### getTicker `GET /v1/pubticker/{symbol}` · Public Returns recent trading activity for a symbol: best bid/ask, last trade price, and 24h volume. ```ts const ticker = await client.marketData.getTicker({ symbol: "BTCUSD" }); console.log(ticker.bid); // "50123.45" console.log(ticker.ask); // "50125.00" console.log(ticker.last); // "50124.50" console.log(ticker.volume); // { timestamp: 1483018200000, price_symbol: "USD", quantity_symbol: "BTC" } ``` > **Note:** Gemini recommends using [getTickerV2](#gettickerv2) for new integrations. This v1 endpoint is maintained for backward compatibility. ### getTickerV2 `GET /v2/ticker/{symbol}` · Public Returns enriched ticker data including open/high/low/close prices, hourly price change snapshots, and the current best bid/ask. ```ts const ticker = await client.marketData.getTickerV2({ symbol: "BTCUSD" }); console.log(ticker.open); // "49800.00" console.log(ticker.high); // "50500.00" console.log(ticker.low); // "49600.00" console.log(ticker.close); // "50347.66" console.log(ticker.bid); // "50345.70" console.log(ticker.ask); // "50347.67" console.log(ticker.changes); // array of 24 hourly price snapshots (strings) ``` > **Tip:** The `changes` array contains 24 decimal-string entries representing hourly closing prices over the last 24 hours, newest first. ### listPrices `GET /v1/pricefeed` · Public Returns a snapshot of the latest price and 24h percentage change for every trading pair. Useful for building dashboards or price tickers. ```ts const prices = await client.marketData.listPrices(); for (const entry of prices) { console.log(entry.pair, entry.price, entry.percentChange24h); // "BTCUSD" "50123.00" "5.23" } ``` > **Tip:** All values in the response are strings. The `percentChange24h` field is a decimal string representing the percentage (e.g. `"5.23"` means +5.23%). ### listFeePromos `GET /v1/feepromos` · Public Returns the list of symbols that currently have active fee promotions. ```ts const promos = await client.marketData.listFeePromos(); console.log(promos); // { symbols: ["BTCGUSD", "ETHGUSD", ...] } ``` > **Tip:** Fee promos change over time. Check this endpoint periodically if your strategy factors in trading fees. ## What's next - [Books, Trades & Candles](/tools/typescript-sdk/reference/market-data/books-trades-candles) — order book snapshots, trade history, and OHLCV candles - [Networks & Derivatives](/tools/typescript-sdk/reference/market-data/networks-and-derivatives) — network/token discovery, FX rates, and funding data - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — why prices are strings and timestamps may be `bigint` - [Error Handling](/tools/typescript-sdk/errors) — how the SDK surfaces API errors - [Full API Specifications](/api-specifications) — complete request/response schemas --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/market-data/networks-and-derivatives.md # TypeScript SDK — Market Data: Networks & Derivatives Network and token discovery, FX rates, and perpetual funding data. All methods are on `client.marketData`. This group mixes public and authenticated endpoints — check the access level on each method. All are `GET`-backed and auto-retry on transient failures (429, 502, 503, 504). ### getAssetsForNetwork `GET /v2/networks/{network}/assets` · Authenticated Returns the enabled assets (tokens) available for deposit and withdrawal on a specified blockchain network, filtered by your account's access permissions. ```ts const result = await client.marketData.getAssetsForNetwork({ network: "ethereum" }); console.log(result.assets); // ["ETH", "USDC", "USDT", ...] ``` The `assets` array is sorted alphabetically and contains only assets where your account has deposit and withdraw access enabled. > **Tip:** Your API key must have the **Fund Manager** or **Auditor** role. See [Authentication](/tools/typescript-sdk/authentication) for key setup. > **Note:** If the network is not supported or has no enabled assets, the API returns a 400 error. ### getTokenNetworkV2 `GET /v2/network/{token}` · Authenticated Returns the blockchain networks available for a given token, filtered by your account's deposit and withdraw permissions. Use this to discover which networks support a token before initiating a deposit or withdrawal. ```ts const result = await client.marketData.getTokenNetworkV2({ token: "USDC" }); console.log(result.network); // ["ethereum", "solana", "base", "arbitrum", "optimism", "avalanche"] ``` The `network` field is always an array and may contain one or more supported networks. > **Tip:** This is the recommended v2 replacement for the retired v1 network endpoint. Your API key must have the **Fund Manager** or **Auditor** role. > **Note:** If the token is not supported or your account has no available networks, the API returns a 404 with `reason: "UnsupportedNetwork"`. ### getFXRate `GET /v2/fxrate/{symbol}/{timestamp}` · Authenticated Returns the FX rate for a given symbol at a specific point in time. Useful for historical reporting and reconciliation. ```ts const rate = await client.marketData.getFXRate({ symbol: "AUDUSD", timestamp: 1594651859000, }); console.log(rate.fxPair); // "AUDUSD" console.log(rate.rate); // 0.69 (number) console.log(rate.asOf); // 1594651859000 console.log(rate.provider); // "bcb" ``` The `timestamp` parameter accepts seconds or milliseconds since the Unix epoch. Gemini strongly recommends using **milliseconds**. The SDK accepts both `bigint` and `number` for this field — see [Data Types](/tools/typescript-sdk/deep-dives/data-types) for details on `bigint` timestamp handling. > **Tip:** The `rate` field is a **number** (not a string). Use a decimal library if you need exact arithmetic with FX rates. ### getFundingAmount `GET /v1/fundingamount/{symbol}` · Public Returns the current and estimated next funding amount for a perpetual symbol. ```ts const funding = await client.marketData.getFundingAmount({ symbol: "BTCGUSDPERP" }); console.log(funding.symbol); // "BTCGUSDPERP" console.log(funding.fundingDateTime); // "2025-04-22T18:00:00.000Z" console.log(funding.fundingTimestampMilliSecs); // 1745344800000 console.log(funding.nextFundingTimestamp); // 1745348400000 console.log(funding.amount); // -1.50991 console.log(funding.estimatedFundingAmount); // -2.10595 ``` > **Tip:** Use this alongside [getRiskStats](/api-specifications) (in the Perpetuals namespace) for a complete view of perp market conditions. ### getNextFundingTimestamp `GET /v1/nextfundingtimestamp/{symbol}` · Public Returns the next funding timestamp for a perpetual symbol as a Unix timestamp in milliseconds. ```ts const nextFundingTimestamp = await client.marketData.getNextFundingTimestamp({ symbol: "BTCGUSDPERP", }); console.log(nextFundingTimestamp); // bigint, e.g. 1745348400000n ``` > **Tip:** The response is an `int64` value and is exposed as a `bigint`. See [Data Types](/tools/typescript-sdk/deep-dives/data-types) for safe timestamp handling. ### getFundingAmountReportFile `GET /v1/fundingamountreport/records.xlsx` · Public Downloads a funding amount report as an Excel file. Unlike most SDK methods, this **returns a file** rather than a parsed JSON response. ```ts const report = await client.marketData.getFundingAmountReportFile({ symbol: "BTCGUSDPERP", fromDate: "2024-04-10", toDate: "2024-04-25", numRows: 1000, }); // report.bytes is a Uint8Array containing the .xlsx file // report.contentType is the MIME type (optional, e.g. "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") // report.contentDisposition is the Content-Disposition header value (optional) import { writeFile } from "node:fs/promises"; await writeFile("funding-report.xlsx", report.bytes); ``` Available query parameters: | Parameter | Type | Required | Description | | --- | --- | --- | --- | | `symbol` | `string` | Yes | Perpetual symbol (e.g. `"BTCGUSDPERP"`). | | `fromDate` | `string` | No | Start date in `YYYY-MM-DD` format. Mandatory if `toDate` is set. | | `toDate` | `string` | No | End date in `YYYY-MM-DD` format. Mandatory if `fromDate` is set. | | `numRows` | `number` | No | Maximum rows to return. Defaults to 8760 if omitted. | When both a date range and `numRows` are specified, the API returns the **minimum** of the records in the date range and `numRows`. > **Caveat:** This method returns `{ bytes: Uint8Array, contentType?: string, contentDisposition?: string }`, not a JSON object. The SDK does not parse the file contents — you receive the raw bytes. > **Tip:** Omitting both date fields fetches up to `numRows` records starting from the present and going backward. ## What's next - [Symbols & Pricing](/tools/typescript-sdk/reference/market-data/symbols-and-pricing) — symbol discovery, ticker data, and price feeds - [Books, Trades & Candles](/tools/typescript-sdk/reference/market-data/books-trades-candles) — order book snapshots, trade history, and OHLCV data - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings, `bigint` timestamps, and safe arithmetic - [Authentication](/tools/typescript-sdk/authentication) — HMAC and OAuth setup for authenticated endpoints - [Error Handling](/tools/typescript-sdk/errors) — how the SDK surfaces API errors - [Full API Specifications](/api-specifications) — complete request/response schemas --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/market-data/books-trades-candles.md # TypeScript SDK — Market Data: Books, Trades & Candles Order book snapshots, public trade history, and OHLCV candlestick data. All methods are on `client.marketData`. All methods in this group are public (no authentication required) and backed by `GET` requests, so they auto-retry on transient failures (429, 502, 503, 504). Prices, quantities, and amounts are **decimal strings**. See [Data Types](/tools/typescript-sdk/deep-dives/data-types) for safe handling. ### getCurrentOrderBook `GET /v1/book/{symbol}` · Public Returns the current order book as two arrays of bid and ask price levels. Each level includes a `price`, `amount`, and `timestamp` — all as strings. ```ts const book = await client.marketData.getCurrentOrderBook({ symbol: "BTCUSD", limit_bids: 10, limit_asks: 10, }); for (const bid of book.bids ?? []) { console.log(bid.price, bid.amount); // "50123.45" "0.5" } for (const ask of book.asks ?? []) { console.log(ask.price, ask.amount); // "50125.00" "1.2" } ``` Parameters are supplied in a single flat options object. Available query parameters: | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `limit_bids` | `number` | 50 | Max bid levels to return. `0` = full book. | | `limit_asks` | `number` | 50 | Max ask levels to return. `0` = full book. | > **Tip:** This endpoint returns a point-in-time snapshot. For a continuously-updated local order book, use the SDK's `client.orderBook(symbol)` helper — see [Order Book Reconstruction](/tools/typescript-sdk/deep-dives/order-book) for how it applies WebSocket depth diffs to stay synchronized. > **Caveat:** Prices and quantities are returned as strings, not numbers. Treating them as floats risks precision loss. See [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### listTrades `GET /v1/trades/{symbol}` · Public Returns public trade history for a symbol, sorted newest first. Each request returns at most 500 records and is limited to seven calendar days of data. ```ts // Most recent 50 trades (default) const trades = await client.marketData.listTrades({ symbol: "BTCUSD" }); // Trades after a specific timestamp, limited to 100 const filtered = await client.marketData.listTrades({ symbol: "BTCUSD", timestamp: 1700000000000n, limit_trades: 100, }); for (const t of filtered) { console.log(t.tid, t.price, t.amount, t.type); // 5335307668 "50124.50" "0.274" "buy" } ``` Available query parameters: | Parameter | Type | Default | Description | | --- | --- | --- | --- | | `timestamp` | `number` | — | Only return trades after this timestamp (seconds or ms since epoch). 90-day hard limit. | | `since_tid` | `number` | — | Only return trades after this trade ID. Overrides `timestamp` if both are set. Use `0` for earliest available data. | | `limit_trades` | `number` | 50 | Maximum trades to return (up to 500). | | `include_breaks` | `boolean` | `false` | Whether to include broken (reversed) trades. | > **Tip:** To poll for new trades (forward pagination), pass the highest `tid` from your last batch as `since_tid`. The API returns trades with IDs strictly greater than the value you provide, so you won't see duplicates. Note that `tid` in the response is a `bigint` while `since_tid` in the query is typed as `number` — convert with `Number(trade.tid)` for values within safe integer range, or use `trade.tid.toString()` and parse back if needed. > **Note:** This endpoint is limited to seven calendar days of data. Contact Gemini for access to extended market data. ### listCandles `GET /v2/candles/{symbol}/{time_frame}` · Public Returns OHLCV (open, high, low, close, volume) candlestick data. Each candle is an array of `[timestamp, open, high, low, close, volume]`. ```ts const candles = await client.marketData.listCandles({ symbol: "BTCUSD", time_frame: "1h", }); for (const [timestamp, open, high, low, close, volume] of candles) { console.log({ timestamp, open, high, low, close, volume }); // { timestamp: 1559755800000, open: 7781.6, high: 7820.23, ... } } ``` Supported `time_frame` values: | Value | Interval | | --- | --- | | `"1m"` | 1 minute | | `"5m"` | 5 minutes | | `"15m"` | 15 minutes | | `"30m"` | 30 minutes | | `"1h"` | 1 hour | | `"6h"` | 6 hours | | `"1d"` | 1 day | > **Note:** Candle values (open, high, low, close, volume) are numbers in this endpoint's response, not strings. This differs from most other market data endpoints. ### listDerivativeCandles `GET /v2/derivatives/candles/{symbol}/{time_frame}` · Public Returns OHLCV candlestick data for perpetual derivative pairs. Currently only the `"1m"` time frame is supported. ```ts const candles = await client.marketData.listDerivativeCandles({ symbol: "BTCGUSDPERP", time_frame: "1m", }); for (const [timestamp, open, high, low, close, volume] of candles) { console.log({ timestamp, open, high, low, close, volume }); } ``` > **Note:** Only `"1m"` is available for derivative candles. Passing any other time frame will result in an error. For spot candles with more time frames, use [listCandles](#listcandles). ## What's next - [Symbols & Pricing](/tools/typescript-sdk/reference/market-data/symbols-and-pricing) — symbol discovery, ticker data, and price feeds - [Networks & Derivatives](/tools/typescript-sdk/reference/market-data/networks-and-derivatives) — network/token lookups, FX rates, and funding data - [Order Book Reconstruction](/tools/typescript-sdk/deep-dives/order-book) — how the SDK maintains a live local order book from WebSocket diffs - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings, `bigint` timestamps, and safe arithmetic - [Full API Specifications](/api-specifications) — complete request/response schemas --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/clearing/instant-orders.md # TypeScript SDK — Clearing: Instant Orders Get instant quotes and execute instant trades. All methods are on `client.instant`. See the [API specifications](/api-specifications) for full request/response schemas and the [authentication guide](/tools/typescript-sdk/authentication) for setup. ## Methods ### getInstantQuote `POST /v1/instant/quote` · Authenticated Requests a price quote for an instant trade. The request uses `totalSpend` to specify the trade size. For **buy** orders, `totalSpend` is the fiat amount you want to spend (e.g. USD). For **sell** orders, `totalSpend` is the **CCY1 quantity** you want to sell (e.g. BTC amount), not a dollar amount. ```ts const quote = await client.instant.getInstantQuote({ symbol: "BTCUSD", side: "buy", totalSpend: "100.00", }); console.log(`Quote: ${quote.price} per BTC, fee: ${quote.fee}`); console.log(`Quantity: ${quote.quantity} ${quote.quantityCurrency}`); console.log(`Total spend: ${quote.totalSpend} ${quote.totalSpendCurrency}`); console.log(`Quote ID: ${quote.quoteId}`); // number — pass this to executeInstantOrder ``` > **Tip:** Quotes are time-limited (`maxAgeMs` tells you how long). Execute promptly or request a fresh quote. `quoteId` is a **number** (not bigint or string). Prices, quantities, and fees are decimal strings. This is a POST mutation — never automatically retried. ### executeInstantOrder `POST /v1/instant/execute` · Authenticated Executes an instant trade using a previously obtained quote. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). The SDK enforces: - `symbol` — required string - `quantity` — required string - `price` — required string - `fee` — required string - `side` — required enum: `"buy"` or `"sell"` - `quoteId` — required number ```ts const result = await client.instant.executeInstantOrder({ symbol: "BTCUSD", side: "buy", quantity: quote.quantity!, price: quote.price!, fee: quote.fee!, quoteId: quote.quoteId!, }); // orderId is optional in the response if (result.orderId != null) { console.log(`Order ID: ${result.orderId}`); } console.log(`Executed: ${result.quantity} ${result.quantityCurrency} @ ${result.price}`); ``` > **Caveat:** This is a POST mutation — never automatically retried. The `quoteId` must reference a valid, unexpired quote from `getInstantQuote`. All string fields (`quantity`, `price`, `fee`) must match the quote response values exactly. The `quoteId` in the request is a `number`. ## Typical instant trade flow 1. Request a quote with `getInstantQuote` using `totalSpend` 2. Display the quote to the user (price, quantity, fees) 3. Execute with `executeInstantOrder`, passing the `quoteId` and the exact `price`, `quantity`, and `fee` from the quote ```ts // 1. Get a quote — specify how much to spend const quote = await client.instant.getInstantQuote({ symbol: "ETHUSD", side: "buy", totalSpend: "500.00", }); // 2. Execute the quote — pass exact values from the quote response const execution = await client.instant.executeInstantOrder({ symbol: "ETHUSD", side: "buy", quantity: quote.quantity!, price: quote.price!, fee: quote.fee!, quoteId: quote.quoteId!, }); ``` ## What's next - [Clearing Orders](/tools/typescript-sdk/reference/clearing/clearing-orders) — standard OTC clearing order management - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and bigint fields --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/clearing/clearing-orders.md # TypeScript SDK — Clearing: Clearing Orders Create, manage, and query OTC clearing orders. All methods are on `client.clearing`. See the [API specifications](/api-specifications) for full request/response schemas and the [authentication guide](/tools/typescript-sdk/authentication) for setup. ## Methods ### createNewClearingOrder `POST /v1/clearing/new` · Authenticated Creates a new OTC clearing order. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). The SDK enforces: - `symbol` — required string - `amount` — required decimal string - `price` — required decimal string - `side` — required enum: `"buy"` or `"sell"` - `counterparty_id` — optional string - `expires_in_hrs` — optional finite number - `account` — optional string (required for Master API keys to target a specific sub-account) > **Note:** If you are using a Master API key, you must include the `account` field to specify which sub-account the clearing order applies to. ```ts const order = await client.clearing.createNewClearingOrder({ symbol: "BTCUSD", amount: "1.0", price: "50000.00", side: "buy", counterparty_id: "counterparty-abc", expires_in_hrs: 24, }); console.log(order.clearing_id); // unique clearing order ID ``` This is a POST mutation — never automatically retried. Prices and amounts must be decimal strings (e.g. `"50000.00"`, not `50000`). ### getClearingOrder `POST /v1/clearing/status` · Authenticated Retrieves the current status of a clearing order by its clearing ID. ```ts const status = await client.clearing.getClearingOrder({ clearing_id: "clearing-123", }); console.log(`Status: ${status.status}, Symbol: ${status.symbol}`); ``` This is a POST mutation — never automatically retried. ### cancelClearingOrder `POST /v1/clearing/cancel` · Authenticated Cancels an active clearing order. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation) — `clearing_id` is a required string. ```ts const result = await client.clearing.cancelClearingOrder({ clearing_id: "clearing-123", }); console.log(result.result); // e.g. "ok" console.log(result.details); // description of the result ``` This is a POST mutation — never automatically retried. Only orders in a cancellable state can be cancelled. ### confirmClearingOrder `POST /v1/clearing/confirm` · Authenticated Confirms a clearing order, finalizing the trade. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). The SDK enforces: - `clearing_id` — required string - `symbol` — required string - `amount` — required decimal string - `price` — required decimal string - `side` — required enum: `"buy"` or `"sell"` ```ts const result = await client.clearing.confirmClearingOrder({ clearing_id: "clearing-123", symbol: "BTCUSD", amount: "1.0", price: "50000.00", side: "buy", }); console.log(result.result); // e.g. "ok" ``` This is a POST mutation — never automatically retried. Confirmation is irreversible — ensure the fields match the original order before confirming. ### listClearingOrders `POST /v1/clearing/list` · Authenticated Lists clearing orders with optional time-range filtering. The response is a **wrapper object** with an `orders` array — not a bare array. Each order uses `quantity` (number) for the amount and `price` (number) for the price. ```ts const response = await client.clearing.listClearingOrders({}); for (const order of response.orders ?? []) { console.log(`${order.clearing_id}: ${order.symbol} ${order.side} ${order.quantity} @ ${order.price}`); } ``` > **Tip:** Timestamp filters (`expiration_start`, `expiration_end`, `submission_start`, `submission_end`) accept `bigint` or `number`. Note that `price` and `quantity` in the list response are **numbers**, not strings. ### listClearingTrades `POST /v1/clearing/trades` · Authenticated Returns a list of executed clearing trades. The response is a **wrapper object** with a `results` array. Trades use `pair` (not `symbol`) and `quantity` (not `amount`). ```ts const response = await client.clearing.listClearingTrades({}); for (const trade of response.results ?? []) { console.log(`${trade.pair}: ${trade.quantity} @ ${trade.price} (${trade.sourceSide})`); } ``` > **Tip:** The request body accepts an optional `timestamp` (bigint or number) for pagination. Each trade includes `clearingId`, `sourceAccount`, `targetAccount`, and status timestamps (`createdMs`, `lastUpdatedMs`, `expirationTimeMs`). ### listClearingBrokers `POST /v1/clearing/broker/list` · Authenticated Lists broker clearing orders. The response is a **wrapper object** with an `orders` array. Broker orders use `source_counterparty_id` (not `counterparty_id`) and `source_side` (not `side`). ```ts const response = await client.clearing.listClearingBrokers({}); for (const order of response.orders ?? []) { console.log(`${order.clearing_id}: ${order.source_counterparty_id} → ${order.target_counterparty_id}`); console.log(` ${order.symbol} ${order.source_side} ${order.quantity} @ ${order.price}`); } ``` > **Tip:** Timestamp filters accept `bigint` or `number` values. Like `listClearingOrders`, `price` and `quantity` are **numbers** in the response. ### createNewBrokerOrder `POST /v1/clearing/broker/new` · Authenticated Creates a new clearing order as a broker on behalf of two counterparties. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). The SDK enforces: - `source_counterparty_id` — required string - `target_counterparty_id` — required string - `symbol` — required string - `amount` — required decimal string - `price` — required decimal string - `side` — required enum: `"buy"` or `"sell"` - `expires_in_hrs` — required finite number ```ts const order = await client.clearing.createNewBrokerOrder({ source_counterparty_id: "counterparty-a", target_counterparty_id: "counterparty-b", symbol: "BTCUSD", amount: "5.0", price: "50000.00", side: "buy", expires_in_hrs: 48, }); console.log(order.clearing_id); // unique clearing order ID console.log(order.result); // "AwaitSourceTargetConfirm" ``` This is a POST mutation — never automatically retried. All amounts and prices must be decimal strings. `expires_in_hrs` must be a finite number (not a string). ## What's next - [Instant Orders](/tools/typescript-sdk/reference/clearing/instant-orders) — instant quote and execution - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and bigint fields --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/account-services/withdrawals-and-transfers.md # TypeScript SDK — Account Services: Withdrawals & Transfers Withdraw crypto, transfer between accounts, estimate gas fees, and review transfer history. All methods are on `client.transfers`. See the [API specifications](/api-specifications) for full request/response schemas and the [authentication guide](/tools/typescript-sdk/authentication) for setup. ## Methods ### withdrawCryptoFunds `POST /v2/withdraw/{network}/{ticker}` · Authenticated Initiates a cryptocurrency withdrawal. This is a security-sensitive mutation — the request is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation) before sending. The SDK enforces: - `network`, `ticker` — required strings - `address` — required string - `amount` — required decimal string - `clientTransferId` — optional UUID - `memo` — optional string ```ts const withdrawal = await client.transfers.withdrawCryptoFunds({ network: "ethereum", ticker: "ETH", address: "0x1234567890abcdef1234567890abcdef12345678", amount: "1.5", clientTransferId: "550e8400-e29b-41d4-a716-446655440000", }); ``` This is a POST mutation — never automatically retried. The optional `clientTransferId` (UUID) provides withdrawal idempotency: duplicate requests with the same ID do not create additional withdrawals. The destination must be on your [approved address list](/tools/typescript-sdk/reference/account-services/addresses-and-deposits). Amounts are decimal strings, not numbers. ### transferBetweenAccounts `POST /v1/account/transfer/{currency}` · Authenticated Transfers funds between accounts within the same master group. The request is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). The SDK enforces: - `currency` — required string - `sourceAccount`, `targetAccount` — required strings - `amount` — required decimal string - `clientTransferId` — optional UUIDv4 ```ts const transfer = await client.transfers.transferBetweenAccounts({ currency: "btc", sourceAccount: "primary", targetAccount: "trading", amount: "0.25", clientTransferId: "550e8400-e29b-41d4-a716-446655440000", }); console.log(transfer.message); // "Success, transfer completed." ``` > **Tip:** When provided, `clientTransferId` must be a UUIDv4. It identifies the transfer request; the API documentation does not promise duplicate suppression, so confirm the transfer status before retrying an uncertain request. ### getGasFeeEstimation `POST /v2/withdraw/{network}/{ticker}/feeEstimate` · Authenticated Returns an estimated gas fee for a withdrawal without actually executing it. ```ts const estimate = await client.transfers.getGasFeeEstimation({ network: "ethereum", ticker: "ETH", address: "0x1234567890abcdef1234567890abcdef12345678", amount: "1.0", }); // fee is a number, not a string console.log(`Estimated fee: ${estimate.fee} ${estimate.currency}`); console.log(`Free withdrawals remaining: ${estimate.monthlyRemaining}`); ``` > **Tip:** Call this before `withdrawCryptoFunds` to show users the expected fee. The `fee` field is a **number** (not a string). `monthlyLimit` and `monthlyRemaining` track free withdrawal allowances. ### listPastTransfers `POST /v2/transfers` · Authenticated Returns a history of past transfers (deposits, withdrawals, and internal transfers). The response is a bare array of `V2Transfer` objects. ```ts const transfers = await client.transfers.listPastTransfers({}); for (const t of transfers) { // t.eid may be bigint console.log(`${t.type}: ${t.amount} ${t.currency} (eid: ${t.eid})`); } ``` > **Tip:** The `eid` field in the response may be a `bigint`. The request body accepts optional `timestamp` (bigint/number) for pagination. ### getTransactionHistory `POST /v1/transactions` · Authenticated Returns detailed transaction history with filtering support. The response is a **wrapper object** with a `results` array. Response fields like `eid`, `tid`, `orderId`, and other IDs may be `bigint`. ```ts const history = await client.transfers.getTransactionHistory({ limit: 50, }); for (const tx of history.results ?? []) { // Transaction entries are either trade or transfer records, with // operation-specific fields. IDs may be bigint. const currency = "currency" in tx ? tx.currency : "symbol" in tx ? tx.symbol : undefined; const eventId = "eid" in tx ? tx.eid : undefined; console.log(`${tx.amount ?? ""} ${currency ?? ""} (eid: ${eventId ?? "n/a"})`); } ``` > **Tip:** The `timestamp_nanos` request field accepts `bigint` for nanosecond-precision filtering. Use `continuation_token` from the response for pagination. Many response ID fields are `bigint` — see [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### listCustodyFeeTransfers `POST /v1/custodyaccountfees` · Authenticated Returns custody fee transfers for custody accounts. The request body accepts an optional `timestamp` field (bigint or number) for filtering. The response is a bare array. ```ts const fees = await client.transfers.listCustodyFeeTransfers({}); for (const fee of fees) { console.log(`${fee.feeAmount} ${fee.feeCurrency} — ${fee.eventType}`); } ``` > **Tip:** Fee amounts are decimal strings. Pass `timestamp` as a bigint or number to filter results — the API returns records **on or after** the given timestamp (lower bound, forward paging), not backward. ## What's next - [Addresses & Deposits](/tools/typescript-sdk/reference/account-services/addresses-and-deposits) — managing deposit and approved withdrawal addresses - [Balances & Account](/tools/typescript-sdk/reference/account-services/balances-and-account) — checking balances - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation catches errors early - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and bigint fields --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/account-services/staking.md # TypeScript SDK — Account Services: Staking Stake and unstake crypto, view staking balances, rates, rewards, and event history. All methods are on `client.staking`. See the [API specifications](/api-specifications) for full request/response schemas and the [authentication guide](/tools/typescript-sdk/authentication) for setup. ## Methods ### listStakingBalances `POST /v1/balances/staking` · Authenticated Returns staking balances for all staked assets in the account. The response is a bare array of `StakingBalance` objects. ```ts const balances = await client.staking.listStakingBalances({}); for (const entry of balances) { // balance, available, and availableForWithdrawal are numbers console.log(`${entry.currency}: ${entry.balance} staked`); } ``` > **Tip:** `balance`, `available`, and `availableForWithdrawal` are **numbers** (not strings). The `balanceByProvider` field is an object keyed by provider UUID. ### listStakingRates `GET /v1/staking/rates` · Public Returns current staking rates for all supported assets. This is the **only public GET** in the account services namespace — it requires no authentication and takes no arguments. The response is a **nested object** keyed by provider UUID, then by currency symbol — not a flat array. ```ts // No auth needed — works with an unauthenticated client const rates = await client.staking.listStakingRates(); // rates is keyed by provider UUID, then by currency for (const [providerId, currencies] of Object.entries(rates)) { for (const [currency, rate] of Object.entries(currencies as Record)) { console.log(`${currency}: ${rate.ratePct}% (provider: ${providerId})`); } } ``` > **Tip:** As a GET operation, this is automatically retried on transient errors (429, 502, 503, 504). All other account services methods are POST mutations that are never retried. ### listStakingRewards `POST /v1/staking/rewards` · Authenticated Returns staking reward history for the account. The response is a **nested object** keyed by provider UUID, then by currency symbol — the same nested structure as `listStakingRates`. ```ts const rewards = await client.staking.listStakingRewards({ since: "2024-01-01T00:00:00.000Z", }); // rewards is keyed by provider UUID, then by currency for (const [providerId, currencies] of Object.entries(rewards)) { for (const [currency, reward] of Object.entries(currencies as Record)) { console.log(`${currency}: ${reward.accrualTotal} earned (provider: ${providerId})`); } } ``` This is a POST mutation — never automatically retried. ### listStakingEventHistory `POST /v1/staking/history` · Authenticated Returns a chronological history of staking events (stakes, unstakes, rewards). The default sort order is **newest first**. The request body accepts optional `since` and `until` fields (bigint or number) for time-range filtering. The response is an array of `StakingHistory` objects, each containing a `providerId` and a `transactions` array. ```ts const history = await client.staking.listStakingEventHistory({ since: 1700000000000, }); for (const provider of history) { console.log(`Provider: ${provider.providerId}`); for (const tx of provider.transactions ?? []) { console.log(` ${tx.transactionType}: ${tx.amount} ${tx.amountCurrency}`); } } ``` > **Tip:** `since` and `until` accept bigint or number values for timestamp-based pagination. Each `StakingTransaction` has `transactionType` (e.g. `"Deposit"`, `"Redeem"`, `"Interest"`), `amount` (number), and `amountCurrency`. ### stakeCryptoFunds `POST /v1/staking/stake` · Authenticated Stakes cryptocurrency with a staking provider. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). The SDK enforces: - `providerId` — required string - `currency` — required string - `amount` — required decimal string ```ts const result = await client.staking.stakeCryptoFunds({ providerId: "62b21e17-2534-4b9f-afcf-b7edb609dd8d", currency: "ETH", amount: "10.0", }); console.log(result.transactionId); // unique staking transaction ID ``` This is a POST mutation — never automatically retried. Staking may have lock-up periods depending on the provider and asset. Amounts must be quoted decimal strings (e.g. `"10.0"`, not `10`). ### unstakeCryptoFunds `POST /v1/staking/unstake` · Authenticated Initiates an unstaking request. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation) with the same rules as `stakeCryptoFunds`: - `providerId` — required string - `currency` — required string - `amount` — required decimal string ```ts const result = await client.staking.unstakeCryptoFunds({ providerId: "62b21e17-2534-4b9f-afcf-b7edb609dd8d", currency: "ETH", amount: "5.0", }); console.log(result.transactionId); // unique unstaking transaction ID ``` This is a POST mutation — never automatically retried. Unstaking may be subject to a cooldown period before funds become available. ## What's next - [Balances & Account](/tools/typescript-sdk/reference/account-services/balances-and-account) — check available and staking balances - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — decimal strings and bigint fields - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/account-services/oauth.md # TypeScript SDK — Account Services: OAuth Token Revocation Low-level OAuth token revocation endpoint. This method is on `client.account`. See the [API specifications](/api-specifications) for the full request/response schema and the [authentication guide](/tools/typescript-sdk/authentication) for OAuth setup. ## Methods ### revokeOAuthToken `POST /v1/oauth/revokeByToken` · Authenticated Revokes the current OAuth access and refresh tokens. This is the **low-level REST endpoint** — most users should use `OAuthAuth.revoke()` instead, which handles token lifecycle, store cleanup, and calls this endpoint under the hood. ```ts // Low-level: direct REST call const result = await client.account.revokeOAuthToken({}); console.log(result.message); // confirmation message ``` This is a POST mutation — never automatically retried. The preferred approach uses the `OAuthAuth` class directly. It owns the revocation request and uses the same configured environment as the OAuth client: ```ts import { OAuthAuth } from "@gemini-markets/sdk/server"; const auth = new OAuthAuth({ client: { type: "public", clientId: "your-client-id", redirectUri: "http://localhost:3000/callback" }, env: "sandbox", tokenStore: yourTokenStore, }); await auth.revoke(); // Tokens are revoked server-side and cleared from your token store ``` > **When to use each:** > > - **`OAuthAuth.revoke()`** — the recommended path. It loads the stored access token as-is, calls the revocation endpoint, and clears the token store in a `finally` block. It does not refresh an expired token; if the server request fails, the local store is still cleared. Use this for logout flows, token rotation, and session cleanup. > - **`client.account.revokeOAuthToken({})`** — the raw REST call. Use only if you need direct control over the HTTP request (e.g. custom error handling, auditing) and are managing token store cleanup yourself. > **Caveat:** After revocation, the access token and refresh token are both invalidated. You'll need to complete a new authorization flow to obtain fresh tokens. ## What's next - [Authentication](/tools/typescript-sdk/authentication) — OAuth setup, HMAC auth, and the `OAuthAuth` class - [Error Handling](/tools/typescript-sdk/errors) — `OAuthTokenError` and `OAuthAuthorizationError` --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/account-services/banking.md # TypeScript SDK — Account Services: Banking & Payments Link bank accounts and manage payment methods. All methods are on `client.account`. See the [API specifications](/api-specifications) for full request/response schemas and the [authentication guide](/tools/typescript-sdk/authentication) for setup. ## Methods ### addBank `POST /v1/payments/addbank` · Authenticated Links a US bank account for fiat deposits and withdrawals. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). The SDK enforces: - `accountnumber` — required string - `routing` — required string - `name` — required string (account holder name) - `type` — required enum: `"checking"` or `"savings"` ```ts const result = await client.account.addBank({ accountnumber: "123456789", routing: "021000021", name: "Jane Doe", type: "checking", }); ``` This is a POST mutation — never automatically retried. Double-check account details before submitting; linking an incorrect account requires manual correction. ### addBankCAD `POST /v1/payments/addbank/cad` · Authenticated Links a Canadian bank account for CAD deposits and withdrawals. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). The SDK enforces: - `swiftcode` — required string - `accountNumber` — required string - `name` — required string - `type` — required enum: `"checking"` or `"savings"` - `institutionNumber`, `branchnnumber` — optional strings ```ts const result = await client.account.addBankCAD({ swiftcode: "ROYCCAT2", accountNumber: "1234567", name: "Jane Doe", type: "checking", institutionNumber: "003", branchnnumber: "00012", }); ``` > **Note:** The field name `branchnnumber` (with double "n") matches the API specification exactly. ### listPaymentMethods `POST /v1/payments/methods` · Authenticated Returns all payment methods linked to the account. The response is a **wrapper object** with `balances` and `banks` arrays — not a bare array. ```ts const methods = await client.account.listPaymentMethods({}); // Fiat balances available for trading for (const balance of methods.balances ?? []) { console.log(`${balance.currency}: ${balance.available} available`); } // Linked bank accounts for (const bank of methods.banks ?? []) { console.log(`${bank.bank} (${bank.bankId})`); } ``` This is a POST mutation — never automatically retried. ## What's next - [Balances & Account](/tools/typescript-sdk/reference/account-services/balances-and-account) — account details and balance queries - [Withdrawals & Transfers](/tools/typescript-sdk/reference/account-services/withdrawals-and-transfers) — moving funds - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/account-services/balances-and-account.md # TypeScript SDK — Account Services: Balances & Account Account details, balances, roles, and account management. All methods are on `client.account`. See the [API specifications](/api-specifications) for full request/response schemas and the [authentication guide](/tools/typescript-sdk/authentication) for setup. ## Methods ### getAccountDetail `POST /v1/account` · Authenticated Returns details about the authenticated account including name, type, and associated users. ```ts const detail = await client.account.getAccountDetail({}); console.log(detail.account?.accountName); // e.g. "Primary" console.log(detail.memo_reference_code); // wire memo reference code ``` This is a POST mutation — never automatically retried. The empty object `{}` is required — the SDK adds transport fields (`nonce`, `request`) automatically. ### getAvailableBalances `POST /v1/balances` · Authenticated Returns the available balances for each currency in your account. The `account` field is **required** in the generated TypeScript type. The response is a bare array of `Balance` objects. ```ts const balances = await client.account.getAvailableBalances({ account: "primary", }); for (const entry of balances) { // entry.amount and entry.available are numbers, not strings console.log(`${entry.currency}: ${entry.available} available`); } ``` > **Tip:** `amount` and `available` are **numbers** (not strings). Other fields like `availableForWithdrawal`, `pendingWithdrawal`, and `pendingDeposit` are also numbers. The `_timestamp` field is an ISO 8601 string for staleness detection — see [Data Types](/tools/typescript-sdk/deep-dives/data-types). ### getNotionalBalances `POST /v1/notionalbalances/{currency}` · Authenticated Returns balances denominated in the specified fiat currency. ```ts const balances = await client.account.getNotionalBalances({ currency: "usd", }); for (const entry of balances) { console.log(`${entry.currency}: ${entry.amountNotional} USD`); } ``` > **Tip:** The `currency` field selects the denomination (e.g. `"usd"`, `"gbp"`, `"eur"`). Notional values (`amountNotional`, `availableNotional`, `availableForWithdrawalNotional`) are decimal strings. ### getRoles `POST /v1/roles` · Authenticated Returns the roles assigned to the API key making the request. ```ts const roles = await client.account.getRoles({}); console.log(roles.isTrader); // boolean console.log(roles.isFundManager); // boolean console.log(roles.isAuditor); // boolean ``` ### listAccountsInGroup `POST /v1/account/list` · Authenticated Lists all accounts in the master group. Useful for multi-account setups. The response is a bare array. ```ts const accounts = await client.account.listAccountsInGroup({}); for (const acct of accounts) { console.log(`${acct.name} (${acct.account}) — ${acct.status}`); } ``` > **Tip:** The `timestamp` field in the request body accepts `bigint` or `number`. ### createNewAccount `POST /v1/account/create` · Authenticated Creates a new sub-account within your account group. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation). ```ts const result = await client.account.createNewAccount({ name: "strategy-2", type: "exchange", }); console.log(result.account); // e.g. "strategy-2" console.log(result.type); // "exchange" or "custody" ``` This is a POST mutation — never automatically retried. Account creation is idempotent by name — creating an account with an existing name returns the existing account. ### renameAccount `POST /v1/account/rename` · Authenticated Renames a sub-account. The request body is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation) — the SDK checks field types before sending. ```ts const result = await client.account.renameAccount({ account: "my-account", newName: "primary-trading", }); ``` This is a POST mutation — never automatically retried. If the request fails mid-flight you should confirm the rename status before retrying. ## What's next - [Addresses & Deposits](/tools/typescript-sdk/reference/account-services/addresses-and-deposits) — deposit addresses and approved address management - [Withdrawals & Transfers](/tools/typescript-sdk/reference/account-services/withdrawals-and-transfers) — moving funds out or between accounts - [Data Types](/tools/typescript-sdk/deep-dives/data-types) — why balances are numbers and timestamps may be bigint - [Error Handling](/tools/typescript-sdk/errors) — handling API errors --- URL: https://developer.gemini.com/tools/typescript-sdk/reference/account-services/addresses-and-deposits.md # TypeScript SDK — Account Services: Addresses & Deposits Manage deposit addresses and the approved withdrawal address allowlist. All methods are on `client.account` and use a unified flat parameter object. See the [API specifications](/api-specifications) for full request/response schemas and the [authentication guide](/tools/typescript-sdk/authentication) for setup. ## Methods ### createNewDepositAddress `POST /v1/deposit/{network}/newAddress` · Authenticated · Fund Manager role · OAuth scope `addresses:create` Generates a new deposit address for the specified network. Requires the **Fund Manager** role. The request is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation) — the SDK checks optional field types (`label`, `legacy`, `account`) before sending. The `account` field is optional for account-level API keys but **required** for Master API keys to target a specific sub-account. ```ts const address = await client.account.createNewDepositAddress({ network: "bitcoin", label: "cold-storage-inbound", }); console.log(address.address); // the new deposit address string ``` This is a POST mutation — never automatically retried. Creating duplicate addresses is safe — each call generates a distinct address. ### listDepositAddresses `POST /v1/addresses/{network}` · Authenticated · Trader, Fund Manager, or Auditor role · OAuth scope `addresses:read` or `addresses:create` Lists all previously generated deposit addresses for the specified network. The response is a bare array of `Address` objects. ```ts const addresses = await client.account.listDepositAddresses({ network: "ethereum", }); for (const addr of addresses) { console.log(`${addr.address} — ${addr.label ?? "(no label)"}`); } ``` ### createNewApprovedAddress `POST /v1/approvedAddresses/{network}/request` · Authenticated · Fund Manager role · Trusted IP required · API key only Requests approval of a new withdrawal address for the specified network. Requires the **Fund Manager** role and **Trusted IP** controls enabled on your account. This endpoint is **API-key only** — it is not accessible via OAuth. The request is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation) — `address` and `label` are required strings. ```ts const result = await client.account.createNewApprovedAddress({ network: "ethereum", address: "0x1234567890abcdef1234567890abcdef12345678", label: "treasury-wallet", }); ``` > **Caveat:** Every newly approved address is subject to a **mandatory seven-day approval hold** before it can be used for withdrawals. This hold always applies and cannot be skipped. Plan withdrawal workflows accordingly. ### listApprovedAddresses `POST /v1/approvedAddresses/account/{network}` · Authenticated · OAuth scope `addresses:read` Lists all approved withdrawal addresses for the specified network on your account. Available to **any API-key role** (Trader, Fund Manager, or Auditor). OAuth callers require the `addresses:read` scope. The response is a **wrapper object** with an `approvedAddresses` array — not a bare array. ```ts const response = await client.account.listApprovedAddresses({ network: "bitcoin", }); for (const entry of response.approvedAddresses ?? []) { console.log(`${entry.address} — ${entry.label} (${entry.status})`); } ``` ### removeApprovedAddress `POST /v1/approvedAddresses/{network}/remove` · Authenticated · Fund Manager role · OAuth scope `addresses:create` Removes an address from the approved withdrawal allowlist. Requires the **Fund Manager** role. OAuth callers require the `addresses:create` scope. The request is [validated client-side](/tools/typescript-sdk/deep-dives/request-validation) — `address` is a required string. ```ts const result = await client.account.removeApprovedAddress({ network: "ethereum", address: "0x1234567890abcdef1234567890abcdef12345678", }); console.log(result.message); // confirmation message ``` > **Caveat:** Removing an approved address is irreversible via this call. You'll need to re-approve the address (with its waiting period) to use it again. ## Flat Unified Parameter Objects Every method on this page takes a single flat object with path parameters (`network`) and any body properties directly at top level: ```ts await client.account.createNewDepositAddress({ network: "ethereum", label: "my-deposit-label", }); ``` See the [patterns guide](/tools/typescript-sdk/patterns) for more on input shapes. ## What's next - [Balances & Account](/tools/typescript-sdk/reference/account-services/balances-and-account) — account details and balance queries - [Withdrawals & Transfers](/tools/typescript-sdk/reference/account-services/withdrawals-and-transfers) — moving funds - [Request Validation](/tools/typescript-sdk/deep-dives/request-validation) — how client-side validation works --- URL: https://developer.gemini.com.md # Gemini Developer Platform --- URL: https://developer.gemini.com/get-started.md # Get started with Gemini > **AI/LLM users:** Fetch the complete documentation index at [developer.gemini.com/llms.txt](https://developer.gemini.com/llms.txt) and the machine-readable API spec catalog at [developer.gemini.com/specs/index.json](https://developer.gemini.com/specs/index.json). Gemini's developer platform unifies multiple trading products into one integration. First choose what you want to trade, then select the required account and interface. Credential rules and account mechanics vary by product and protocol. Follow the authentication and product documentation for your specific workflow. ## Start with the platform workflow 1. [Choose a trading product](/#products). 2. [Create credentials](/authentication/api-key) for your interface. 3. Identify the target trading account. 4. Verify that the account has required permissions. 5. Test your integration in the [demo environment](/get-started/sandbox). 6. Find the exact operation in the [API Reference](/api-reference). ## What you can build - **Trading applications** — Execute orders across supported trading products - **Market data tools** — Access real-time prices and order books - **Portfolio trackers** — Monitor balances and transaction history - **Prediction market bots** — Trade event contracts programmatically - **Agent workflows** — Build with Gemini's open-source MCP server and packaged agent skills ## Choose a trading product - [Spot crypto](/products/spot) - [Margin](/products/margin) - [Perpetuals](/products/perpetuals) - [Prediction markets](/products/prediction-markets) Stock trading is available in the Gemini UI. API trading documentation is coming soon. ## Choose an interface - **REST** for request-response workflows and account operations - **WebSocket** for streaming data and real-time trading - **FIX** for institutional trading workflows - **MCP server, API samples, and agent skills** are [available now](/tools); SDK packages and the Gemini API CLI are in development Interface support varies by product. Use each product overview and the [API Reference](/api-reference) to find supported endpoints. ## Understand accounts and access Credentials authenticate your requests. Your selected account determines where operations occur, while product permissions control access. Assigned roles and OAuth scopes govern permitted actions. Account mechanics differ across interfaces. Review the [Platform overview](/platform), [API key authentication](/authentication/api-key), [OAuth](/authentication/oauth), and [roles](/roles) before implementing private operations. ## Ready to start? - [Get API keys →](/authentication/api-key) - [Use OpenAPI/AsyncAPI specs →](/api-specifications) - [Explore the demo environment →](/get-started/sandbox) - [Choose a product →](/#products) - [Explore API Reference →](/api-reference) --- URL: https://developer.gemini.com/products/prediction-markets.md # Prediction Markets