HTTP API

The HTTP API exposes the same market data and paper-trading actions you see in the dashboard, scoped to your account and authenticated with a personal API key. Designed for scripts, backtests, and any client that doesn't want to go through OAuth (Claude / Cursor / Codex should use MCP instead — it's already wired).

Base URL

https://gex.junk.place/api/v1

Every endpoint below lives under that prefix. Everything is JSON over HTTPS, no versioned media types — just plain Content-Type: application/json on requests with a body.

Authentication

Send your API key in the standard Authorization: Bearer … header on every request:

curl -H "Authorization: Bearer gex_live_…" \
     https://gex.junk.place/api/v1/me

Keys are issued from /account/api-keys. Each key has a name (so you can tell them apart later), a list of scopes, and shows up with its last-used time + IP. There is no expiry — rotate by issuing a new key and revoking the old one. The plaintext token is shown only once, at creation; the server stores its SHA-256 hash.

Errors

StatusError codeMeaning
401missing_bearerNo Authorization header.
401invalid_keyToken malformed, unknown, or revoked.
403insufficient_scopeThe key is valid but doesn't include this scope.
400invalid_requestBody or query failed validation (details field).
404not_found / variousAccount / position / expiry doesn't exist for you.

Scopes

A key carries an explicit list of scopes. Pick the minimum your script needs — there's no "all" scope and never will be.

  • analytics:read — public market data: expirations, snapshots, timeline.
  • portfolio:read — your paper accounts, positions, history, transactions.
  • portfolio:write — open and close positions in your paper accounts.

Scope checks are per-endpoint. A portfolio:read key gets 403 on write endpoints — the spelling of the missing scope is in the response body so you know exactly what to enable.

Endpoints

Identity

GET /api/v1/me

Scope: any. Returns the user that owns the key and which scopes it has. Useful as a smoke test after generating a key.

curl -H "Authorization: Bearer gex_live_…" \
     https://gex.junk.place/api/v1/me
{
  "userId": "65f1e9c7…",
  "keyId":  "65f1e9d2…",
  "scopes": ["analytics:read", "portfolio:read"]
}

Analytics

GET /api/v1/expirations

Scope: analytics:read. Lists every (source, currency, expiry) triple that has a recent snapshot. Use it to discover what's tradeable right now — the dashboard's expiry dropdown is built from this exact call.

curl -H "Authorization: Bearer gex_live_…" \
     https://gex.junk.place/api/v1/expirations
[
  { "source": "deribit", "currency": "BTC", "expiry": "2026-05-30T08:00:00.000Z" },
  { "source": "deribit", "currency": "BTC", "expiry": "2026-06-27T08:00:00.000Z" }
]

GET /api/v1/analytics/{source}/{currency}/{expiry}

Scope: analytics:read. Latest snapshot for one expiry: per-strike call / put OI, IVs, GEX, plus computed metrics (max-pain, gamma flip, walls). {expiry} must be the ISO timestamp returned by /expirations.

curl -H "Authorization: Bearer gex_live_…" \
  "https://gex.junk.place/api/v1/analytics/deribit/BTC/2026-05-30T08:00:00.000Z"

GET /api/v1/timeline/{source}/{currency}/{expiry}?days=7

Scope: analytics:read. The same snapshot at every 5-minute tick for the last days days (default 7, max 30). Drives the timeline chart at the bottom of the dashboard.

Portfolio — read

GET /api/v1/portfolio/accounts

Scope: portfolio:read. Lists your paper accounts.

[
  {
    "accountId": "65f1ea3a…",
    "name": "default",
    "mode": "paper",
    "isDefault": true,
    "balances": { "btc": 0.98, "eth": 0, "usdc": 50000 },
    "reservedCollateral": { "btc": 0.02, "eth": 0, "usdc": 0 },
    "createdAt": "2026-04-01T12:00:00.000Z"
  }
]

GET /api/v1/portfolio/accounts/{id}

Scope: portfolio:read. Full account snapshot: balances, reserved collateral, unrealized P&L, total equity, aggregated greeks per book, and every open position with mark and P&L. The response shape is identical to the one the web UI's Overview tab consumes.

GET /api/v1/portfolio/accounts/{id}/history?limit=50&before=…

Scope: portfolio:read. Closed trades, most recent first. before is an ISO timestamp for cursor-style pagination on exitFilledAt.

GET /api/v1/portfolio/accounts/{id}/transactions?limit=100&before=…&includeFunding=false

Scope: portfolio:read. Every fill, settlement, swap, and (optionally) funding accrual. Funding events are noisy — opt in via includeFunding=true when you actually want them.

Portfolio — write

POST /api/v1/portfolio/accounts/{id}/positions

Scope: portfolio:write. Opens a position at the live mid mark. The server reserves the required collateral and returns the filled position.

curl -X POST \
  -H "Authorization: Bearer gex_live_…" \
  -H "Content-Type: application/json" \
  -d '{
        "instrument": "BTC-30MAY26-110000-C",
        "side": "long",
        "qtyContracts": 0.5
      }' \
  "https://gex.junk.place/api/v1/portfolio/accounts/65f1ea3a…/positions"

Body:

FieldTypeRequiredNotes
instrumentstringyesDeribit instrument name, e.g. BTC-PERPETUAL.
side"long" | "short"yes
qtyContractsnumber > 0yesContract units; fractional allowed.
leverageint 1–50noPerps only; defaults to account setting.
limitPriceCryptonumber > 0noIf set, queues a limit order instead of market.

POST /api/v1/portfolio/accounts/{id}/positions/{posId}/close

Scope: portfolio:write. Closes (or partially closes) a position. Send no body to fully close at mark; include qtyContracts to partial-close.

curl -X POST \
  -H "Authorization: Bearer gex_live_…" \
  -H "Content-Type: application/json" \
  -d '{"qtyContracts": 0.25}' \
  "https://gex.junk.place/api/v1/portfolio/accounts/65f1ea3a…/positions/65f1eb12…/close"

Versioning

Everything lives under /api/v1. We won't break this prefix — additive changes only (new fields, new endpoints). When (if) we ship breaking changes they go under /api/v2, and /api/v1 stays alive long enough for callers to migrate.

MCP alternative

If you're building an AI agent rather than a script, use MCP instead: https://gex.junk.place/mcp over HTTP transport. MCP gives you 30+ tools with structured argument schemas, OAuth-based per-user auth, and no need to hand-write JSON. See the paper-trading walkthrough for the full setup.