# Getting Started

Trade on real-world events — elections, crypto prices, sports — using Gemini's trading infrastructure. By the end of this guide you'll have browsed live markets, streamed real-time prices, and placed your first order.

## Try It Now — No Account Needed

The markets API is public. Open a terminal and run:

```bash
curl "https://api.gemini.com/v1/prediction-markets/events?status=active&limit=3"
```

You just pulled live prediction markets from Gemini. No API key, no signup.

<details>
<summary>New to prediction markets? Here's how they work.</summary>

```
Event                        "2028 Presidential Election Winner"
  └─ Contract                "JD Vance"
       ├─ YES ask: $0.28     ~28% implied probability he wins
       └─ NO  ask: $0.73     ~73% implied probability he doesn't
```

**Events** are real-world questions. Each event contains one or more **contracts** — the possible outcomes you can trade. Every contract has a **YES** and **NO** side.

**The price approximates the probability.** A YES contract asking $0.28 means the market thinks there's roughly a 28% chance that outcome happens. Because of the bid-ask spread, buying both YES and NO will cost slightly more than $1.00 — the difference is the spread.

**Payout is always $1.** Exactly one side of every contract settles at $1, the other at $0. If you buy 100 YES contracts at $0.28, you pay $28. If the outcome happens, you receive $100 ($72 profit). If it doesn't, you lose your $28.

**Settlement is automatic.** When an event resolves, winning contracts pay out $1 each to your account. Losing contracts expire worthless. No action needed.

</details>

## Step 1: Browse Markets

Every response follows the same shape: an **event** containing an array of **contracts**, each with live prices.

```bash
# Filter by category — try crypto, politics, sports, or omit for all
curl "https://api.gemini.com/v1/prediction-markets/events?status=active&category=politics"
```

```json
// Fields omitted for brevity — see full schema in the API reference
{
  "data": [
    {
      "title": "2028 Presidential Election Winner",
      "status": "active",
      "category": "politics",
      "contracts": [
        {
          "label": "JD Vance",
          "instrumentSymbol": "GEMI-PRES2028-VANCE",
          "prices": {
            "bestBid": "0.26",
            "bestAsk": "0.28",
            "lastTradePrice": "0.27"
          }
        }
      ]
    }
  ]
}
```

The field you need for everything that follows is **`instrumentSymbol`** — that's the trading symbol you'll use for WebSocket subscriptions and orders.

| Field | What it means |
|-------|---------------|
| `instrumentSymbol` | Trading symbol (e.g., `GEMI-PRES2028-VANCE`). Use this for subscriptions and orders. |
| `bestBid` / `bestAsk` | Current best buy and sell prices on the order book. |

There are also endpoints for [newly listed](/prediction-markets/markets#list-newly-listed-events), [recently settled](/prediction-markets/markets#list-recently-settled-events), and [upcoming](/prediction-markets/markets#list-upcoming-events) markets — all public, no auth required.

## Step 2: Stream Live Prices

REST gives you snapshots, but prices move fast — especially around news events. The WebSocket streams every price change to you instantly.

| Use Case | Channel | Why |
|----------|---------|-----|
| Browse markets | REST `GET /v1/prediction-markets/events` | One-time lookups |
| Check positions | REST `POST /v1/prediction-markets/positions` | Snapshot queries |
| Live prices | WebSocket `{symbol}@bookTicker` | Real-time, no polling |
| Place/cancel orders | WebSocket `order.place` / `order.cancel` | Lowest latency |
| Order fills | WebSocket `orders@account` | Pushed as they happen |
| Balance changes | WebSocket `balances@account` | Real-time buying power |

### Account Setup (~2 minutes)

Steps 2 and 3 require authentication.

1. Log into the [Gemini Exchange](https://exchange.gemini.com) and accept the Prediction Markets terms of service (you'll see a prompt)
2. Visit [Settings/API](https://exchange.gemini.com/settings/api) and create a key:
   - Scope to your trading account
   - Check the **time-based nonce** box (required for WebSocket auth)
   - Check the **Trading** box (grants `NewOrder` and `CancelOrder`)
3. Save your **API key** and **API secret** — you'll need both below

<Callout type="caution" title="Connection will fail without these settings">
  WebSocket authentication requires **account-scoped** keys with **time-based nonces**. Keys without these settings will be rejected at connection time.
</Callout>

### Connect and Subscribe

Replace `your-api-key` and `your-api-secret` with your credentials, then run:

<CodeTabs>

```javascript title="Node.js"
// npm install ws
const crypto = require("crypto");
const WebSocket = require("ws");

const API_KEY = "your-api-key";
const API_SECRET = "your-api-secret";
const SYMBOL = "GEMI-PRES2028-VANCE";

// Authentication — signs a timestamp with your secret so Gemini
// can verify your identity. You don't need to modify this block.
const nonce = Math.floor(Date.now() / 1000).toString();
const payload = Buffer.from(nonce).toString("base64");
const signature = crypto
  .createHmac("sha384", API_SECRET)
  .update(payload)
  .digest("hex");

const ws = new WebSocket("wss://ws.gemini.com", {
  headers: {
    "X-GEMINI-APIKEY": API_KEY,
    "X-GEMINI-NONCE": nonce,
    "X-GEMINI-PAYLOAD": payload,
    "X-GEMINI-SIGNATURE": signature,
  },
});

ws.on("open", () => {
  ws.send(JSON.stringify({
    id: "1",
    method: "subscribe",
    params: [`${SYMBOL}@bookTicker`],
  }));
});

ws.on("message", (raw) => {
  const data = JSON.parse(raw);
  if (data.b && data.a) {
    console.log(`${data.s}  bid: $${data.b} (${data.B} contracts)  ask: $${data.a} (${data.A} contracts)`);
  }
});
```

```python title="Python"
# pip install websockets
import asyncio, websockets, hmac, hashlib, base64, json, time

API_KEY = "your-api-key"
API_SECRET = "your-api-secret"
SYMBOL = "GEMI-PRES2028-VANCE"

# Authentication — signs a timestamp with your secret so Gemini
# can verify your identity. You don't need to modify this block.
def auth_headers():
    nonce = str(int(time.time()))
    payload = base64.b64encode(nonce.encode())
    signature = hmac.new(API_SECRET.encode(), payload, hashlib.sha384).hexdigest()
    return {
        "X-GEMINI-APIKEY": API_KEY,
        "X-GEMINI-NONCE": nonce,
        "X-GEMINI-PAYLOAD": payload.decode(),
        "X-GEMINI-SIGNATURE": signature,
    }

async def stream_prices():
    async with websockets.connect("wss://ws.gemini.com", additional_headers=auth_headers()) as ws:
        await ws.send(json.dumps({
            "id": "1",
            "method": "subscribe",
            "params": [f"{SYMBOL}@bookTicker"],
        }))

        async for msg in ws:
            data = json.loads(msg)
            if "b" in data and "a" in data:
                print(f"{data['s']}  bid: ${data['b']} ({data['B']} contracts)  ask: ${data['a']} ({data['A']} contracts)")

asyncio.run(stream_prices())
```

</CodeTabs>

You should see prices streaming in your terminal:

```
GEMI-PRES2028-VANCE  bid: $0.26 (5000 contracts)  ask: $0.28 (3200 contracts)
GEMI-PRES2028-VANCE  bid: $0.26 (4800 contracts)  ask: $0.28 (3200 contracts)
GEMI-PRES2028-VANCE  bid: $0.27 (1200 contracts)  ask: $0.28 (3200 contracts)
```

If you see prices, your connection and authentication are working. Press `Ctrl+C` to stop, then move on to Step 3.

**[Try it in the playground →](/websocket/playground#method-subscribe)** — subscribe to any stream without writing code.

<Callout type="caution" title="Connection rejected?">
  Auth headers must be sent during the WebSocket handshake — you cannot authenticate after connecting. Double-check that your key is account-scoped with time-based nonces enabled.
</Callout>

## Step 3: Place Your First Trade

Now the real thing. This script connects, subscribes to prices *and* your order updates, waits for a price, then places a limit order.

**Fund your account first**: You need USD in your Gemini account. Start small — buying 10 contracts at $0.27 costs $2.70.

<CodeTabs>

```javascript title="Node.js"
// npm install ws
const crypto = require("crypto");
const WebSocket = require("ws");

const API_KEY = "your-api-key";
const API_SECRET = "your-api-secret";
const SYMBOL = "GEMI-PRES2028-VANCE";

const nonce = Math.floor(Date.now() / 1000).toString();
const payload = Buffer.from(nonce).toString("base64");
const signature = crypto
  .createHmac("sha384", API_SECRET)
  .update(payload)
  .digest("hex");

const ws = new WebSocket("wss://ws.gemini.com", {
  headers: {
    "X-GEMINI-APIKEY": API_KEY,
    "X-GEMINI-NONCE": nonce,
    "X-GEMINI-PAYLOAD": payload,
    "X-GEMINI-SIGNATURE": signature,
  },
});

ws.on("open", () => {
  ws.send(JSON.stringify({
    id: "1",
    method: "subscribe",
    params: [
      `${SYMBOL}@bookTicker`,  // live prices
      "orders@account",         // your order updates
    ],
  }));
});

let orderPlaced = false;

ws.on("message", (raw) => {
  const data = JSON.parse(raw);

  // Once we see a price, place an order
  if (!orderPlaced && data.b && data.a && data.s === SYMBOL) {
    console.log(`Best bid: $${data.b}  Best ask: $${data.a}`);
    console.log("Placing order...");
    orderPlaced = true;

    ws.send(JSON.stringify({
      id: "2",
      method: "order.place",
      params: {
        symbol: SYMBOL,
        side: "BUY",
        type: "LIMIT",
        timeInForce: "GTC",       // Good Til Cancelled — stays open until filled or you cancel it
        price: "0.27",            // your price — set at or above the ask to fill immediately
        quantity: "10",           // number of contracts (costs price x quantity in USD)
        eventOutcome: "YES",      // YES = predicting the event happens, NO = predicting it doesn't
        clientOrderId: "my-first-prediction",
      },
    }));
  }

  // Order lifecycle updates from orders@account stream
  // X = status, S = side, O = outcome, p = price, q = quantity
  if (["NEW", "OPEN", "FILLED", "PARTIALLY_FILLED"].includes(data.X)) {
    console.log(`Order ${data.X}: side=${data.S} outcome=${data.O} price=$${data.p} qty=${data.q}`);
  }
});
```

```python title="Python"
# pip install websockets
import asyncio, websockets, hmac, hashlib, base64, json, time

API_KEY = "your-api-key"
API_SECRET = "your-api-secret"
SYMBOL = "GEMI-PRES2028-VANCE"

def auth_headers():
    nonce = str(int(time.time()))
    payload = base64.b64encode(nonce.encode())
    signature = hmac.new(API_SECRET.encode(), payload, hashlib.sha384).hexdigest()
    return {
        "X-GEMINI-APIKEY": API_KEY,
        "X-GEMINI-NONCE": nonce,
        "X-GEMINI-PAYLOAD": payload.decode(),
        "X-GEMINI-SIGNATURE": signature,
    }

async def trade():
    async with websockets.connect("wss://ws.gemini.com", additional_headers=auth_headers()) as ws:
        await ws.send(json.dumps({
            "id": "1",
            "method": "subscribe",
            "params": [
                f"{SYMBOL}@bookTicker",  # live prices
                "orders@account",         # your order updates
            ],
        }))

        # Wait for a price
        async for msg in ws:
            data = json.loads(msg)
            if "b" in data and "a" in data and data.get("s") == SYMBOL:
                print(f"Best bid: ${data['b']}  Best ask: ${data['a']}")
                print("Placing order...")
                break

        # Place an order
        await ws.send(json.dumps({
            "id": "2",
            "method": "order.place",
            "params": {
                "symbol": SYMBOL,
                "side": "BUY",
                "type": "LIMIT",
                "timeInForce": "GTC",       # Good Til Cancelled — stays open until filled or you cancel it
                "price": "0.27",            # your price — set at or above the ask to fill immediately
                "quantity": "10",           # number of contracts (costs price x quantity in USD)
                "eventOutcome": "YES",      # YES = predicting the event happens, NO = predicting it doesn't
                "clientOrderId": "my-first-prediction",
            },
        }))

        # Watch order updates
        # X = status, S = side, O = outcome, p = price, q = quantity
        async for msg in ws:
            data = json.loads(msg)
            if data.get("X") in ("NEW", "OPEN", "FILLED", "PARTIALLY_FILLED"):
                print(f"Order {data['X']}: side={data.get('S')} outcome={data.get('O')} price=${data.get('p')} qty={data.get('q')}")

asyncio.run(trade())
```

</CodeTabs>

You should see:

```
Best bid: $0.26  Best ask: $0.28
Placing order...
Order NEW: side=BUY outcome=YES price=$0.27 qty=10
Order OPEN: side=BUY outcome=YES price=$0.27 qty=10
```

**[Try it in the playground →](/websocket/playground#method-order.place)** — place orders interactively without writing code.

Your order is now live on the book at $0.27. When someone sells at your price, you'll see:

```
Order FILLED: side=BUY outcome=YES price=$0.27 qty=10
```

### Cancelling Orders

To cancel, send `order.cancel` with the order ID from the `i` field in the order event:

<CodeTabs>

```javascript title="Node.js"
ws.send(JSON.stringify({
  id: "3",
  method: "order.cancel",
  params: { orderId: "73797746498585286" },
}));
```

```python title="Python"
# Inside your async function, on the same ws connection:
await ws.send(json.dumps({
    "id": "3",
    "method": "order.cancel",
    "params": { "orderId": "73797746498585286" }
}))
```

</CodeTabs>

<Callout type="caution" title="Order rejected?">
  **`-2010`**: Price must be between $0.01–$0.99, quantity must be > 0, and `eventOutcome` must be `YES` or `NO`. The market may also be closed.

  **`TERMS_NOT_ACCEPTED`**: Accept the Prediction Markets terms in the [Gemini Exchange](https://exchange.gemini.com) UI first.

  **`InsufficientFunds`**: Your account needs enough USD to cover the order. 10 contracts at $0.27 = $2.70.
</Callout>

## Next Steps

- **[WebSocket playground](/websocket/playground)** — Test every method interactively
- **[Stream reference](/websocket/streams)** — All data streams (depth, trades, order events)
- **[Authentication details](/websocket/authentication)** — Signature generation for other languages
- **[Prediction Markets API](/prediction-markets)** — Full REST API reference
- **[Ticker formats](/prediction-markets/tickers-overview)** — How prediction market symbols are structured
- **[Maker Rebate Program](/prediction-markets/maker-rebate-program)** — Earn rebates by providing liquidity
