obsidiate.

Developers

REST for everything, websockets for everything live — the same data and order flow the terminal runs on.

Overview

The Obsidiate API exposes the same data and order flow the terminal runs on: 99 instruments across crypto, stocks, forex and metals, one consistent interface. Everything is JSON over HTTPS; live data streams over websockets.

Timestamps are Unix seconds, UTC. Prices and quantities are returned as strings to avoid floating-point surprises. Market data endpoints are public; trading endpoints require an API key.

Example
Base URL
https://api.obsidiate.com/v1

Live streams
wss://stream.obsidiate.com

Authentication

Create API keys from your dashboard. Each key carries scopes — read, trade, withdraw — and can be locked to IP addresses. Public market data needs no key at all.

Private requests are signed: send your key, a timestamp, and an HMAC-SHA256 signature of the timestamp, method, path and body, computed with your secret. Requests older than 30 seconds are rejected.

Example
curl https://api.obsidiate.com/v1/balances \
  -H "X-OBX-KEY: obx_live_2f8c91..." \
  -H "X-OBX-TS: 1781119200" \
  -H "X-OBX-SIGN: <hmac_sha256(ts + method + path + body)>"
HeaderValue
X-OBX-KEYYour API key
X-OBX-TSUnix timestamp (seconds) of the request
X-OBX-SIGNHex HMAC-SHA256 over ts + method + path + body

Rate limits

Public endpoints allow 120 requests per minute per IP. Private endpoints use a weighted budget per account that scales with your tier. Exceeding a budget returns 429 with a Retry-After header.

If you are polling market data more than once a second, you want the websocket instead — it is faster, cheaper for everyone, and never rate-limits a subscribed stream.

TierPrivate budget (points / min)
Bronze300
Silver360
Gold480
Platinum720
Diamond1,200

Market data

Public, no key required. Symbols are uppercase tickers — BTC, NVDA, EUR/USD (URL-encode the slash), XAU.

GET/markets

Every listed instrument with its class, name and price precision.

Response
{
  "markets": [
    { "symbol": "BTC", "class": "crypto", "name": "Bitcoin", "decimals": 2 },
    { "symbol": "EUR/USD", "class": "forex", "name": "Euro / US Dollar", "decimals": 4 }
  ]
}
GET/markets/{symbol}

Current quote for one instrument.

Response
{
  "symbol": "BTC",
  "price": "67412.50",
  "change24h": "2.41",
  "high24h": "68011.00",
  "low24h": "66102.20",
  "volume24h": "18412.84",
  "ts": 1781119324
}
GET/markets/{symbol}/klines

Historical candles. One bar per interval; the last bar is the live, still-forming one.

range*1H, 1D, 1W, 1M or 1Y — the window to cover; bar size adapts
limitMax bars to return (default 500)
Request
GET /v1/markets/BTC/klines?range=1D
Response
{
  "symbol": "BTC",
  "bars": [
    { "t": 1781119200, "o": "67318.2", "h": "67501.0", "l": "67255.4", "c": "67412.5", "v": "1284.2" }
  ]
}
GET/markets/{symbol}/book

Order book snapshot, best levels first.

depthLevels per side, 1–100 (default 50)
Response
{
  "symbol": "BTC",
  "bids": [["67410.50", "0.842"], ["67409.00", "1.205"]],
  "asks": [["67412.50", "0.617"], ["67414.00", "2.330"]],
  "ts": 1781119324
}
GET/markets/{symbol}/trades

Most recent public trades, newest first.

limit1–500 (default 100)
Response
{
  "trades": [
    { "id": "t_8k2m1", "price": "67412.50", "qty": "0.0150", "side": "buy", "ts": 1781119324 }
  ]
}

Trading

Requires a key with the trade scope. Orders are accepted, validated against your balance, then matched — the response tells you exactly how far it got.

POST/ordersAPI key

Place an order. Market orders fill immediately against the book; limit orders rest; stop-loss orders trigger a market sell when the stop price trades.

symbol*Instrument ticker, e.g. BTC
side*buy or sell
type*market, limit or stop_loss
qty*Base quantity
priceRequired for limit orders
stopRequired for stop_loss orders
Request
POST /v1/orders
{
  "symbol": "BTC",
  "side": "buy",
  "type": "limit",
  "qty": "0.2500",
  "price": "66800.00"
}
Response
{
  "id": "ord_9f3k2m",
  "status": "open",
  "filled": "0.0000",
  "ts": 1781119325
}
GET/ordersAPI key

Your open orders, optionally filtered by symbol.

symbolFilter to one instrument
Response
{
  "orders": [
    { "id": "ord_9f3k2m", "symbol": "BTC", "side": "buy", "type": "limit", "qty": "0.2500", "price": "66800.00", "filled": "0.0000", "status": "open" }
  ]
}
DELETE/orders/{id}API key

Cancel a resting order. Fills that already happened stay yours.

Response
{
  "id": "ord_9f3k2m",
  "status": "canceled",
  "filled": "0.0834"
}
GET/balancesAPI key

Every balance on the account, including amounts locked in open orders.

Response
{
  "balances": [
    { "asset": "USD", "free": "12480.22", "locked": "16700.00" },
    { "asset": "BTC", "free": "0.4521", "locked": "0.0000" }
  ]
}
GET/fillsAPI key

Your trade history, newest first.

symbolFilter to one instrument
limit1–500 (default 100)
Response
{
  "fills": [
    { "id": "f_2c91x", "order": "ord_9f3k2m", "symbol": "BTC", "side": "buy", "qty": "0.0834", "price": "66800.00", "fee": "1.39", "ts": 1781119388 }
  ]
}

Websocket streams

Connect to wss://stream.obsidiate.com, send one subscribe message, receive ticks until you hang up. The server pings every 30 seconds; reply with a pong or the connection is dropped.

Channels are channel:SYMBOL pairs. Subscribe to as many as you like on one connection.

WSticker:{symbol}

Every price change for an instrument — the same feed the terminal header runs on.

Subscribe
{ "op": "subscribe", "channels": ["ticker:BTC", "ticker:ETH"] }
Message
{ "s": "BTC", "p": 67414.12, "t": 1781119324 }
WSbook:{symbol}

Order book deltas. Apply them to a /book snapshot; a seq gap means resnapshot.

Subscribe
{ "op": "subscribe", "channels": ["book:BTC"] }
Message
{ "s": "BTC", "seq": 882104, "bids": [["67410.50", "0.000"]], "asks": [["67414.00", "1.882"]] }
WStrades:{symbol}

Public trades as they print.

Subscribe
{ "op": "subscribe", "channels": ["trades:BTC"] }
Message
{ "s": "BTC", "p": 67412.50, "q": 0.015, "side": "buy", "t": 1781119324 }

Errors

Every error is the same envelope, with an HTTP status that means what it says. Error codes are stable — match on the code, not the message.

Example
{
  "error": {
    "code": "insufficient_balance",
    "message": "Order requires 16,700.00 USD; 12,480.22 available."
  }
}
StatusCodeMeaning
400invalid_requestMalformed body or parameters
401unauthorizedMissing or bad key, timestamp or signature
403insufficient_scopeKey lacks the required scope
404not_foundUnknown symbol, order or route
409insufficient_balanceNot enough free balance for the order
429rate_limitedBudget exhausted — honor Retry-After
500internalOur fault; safe to retry with backoff

Versioning

The API is versioned in the path. Within v1, changes are additive only — new fields and endpoints may appear, existing ones never change meaning or disappear. Breaking changes get a new version with a long overlap.

Live platform health, including the API and every market-data feed, is published on the status page.

This reference describes v1 as it will ship with the platform; details may change before launch. Questions or early-access requests: hello@obsidiate.com.