API Docs

Full reference for the CryHub REST and WebSocket APIs — auth, schemas, and copy-paste samples in cURL, Node.js, Python, and Go.

Getting started

CryHub API Reference

Real-time crypto market data — unified REST and WebSocket access with multi-currency pricing, order books, klines, trades, and top movers.

REST Base URLhttps://api.cryhub.io/rest/v1
WebSocket URLwss://api.cryhub.io/ws
Building with an AI agent? A condensed, machine-readable version of this whole reference — every endpoint, channel, tick shape and gotcha — is served as plain text at /llms.txt. Point your agent at that URL instead of scraping this page.
Spot + perp coverageEquities · commodities · FXUSDT · USDC · TMN · AED · EURREST + WebSocket
Getting started

Authentication

Every request requires a 32-character access token (letters + digits).

REST — Authorization header

curl https://api.cryhub.io/rest/v1/market/ticker?source=s1&quote=usdt&symbols=btc \
  -H "Authorization: Bearer YOUR_32_CHAR_TOKEN"

WebSocket — token query param

const ws = new WebSocket('wss://api.cryhub.io/ws?token=YOUR_32_CHAR_TOKEN');
Token must be exactly 32 characters and contain only ASCII letters (a-z, A-Z) and digits (0-9). Requests without a valid token are rejected with HTTP 401.
Getting started

Sources & symbols

Every REST call and WebSocket channel is scoped to a source. Each source publishes its own set of quote currencies.

sourceCoverageREST quotesWebSocket quotes
s1Spot feed — cryptousdt · tmn · aed · eurusdt · tmn
s2Perp feed — crypto, equities, commodities, indices, FXusdc · tmnusdc · tmn
AED and EUR are REST-only. Channels such as s1.btcaed.ticker subscribe successfully but never emit a frame. Use REST for those quotes. s2 does not support AED or EUR anywhere.

Fiat quotes (TMN, AED, EUR) are converted server-side with rates refreshed every 4 seconds. Every price in every response is already in the requested quote — no client-side conversion needed.

Symbol format

REST symbols/symbol take base assets only; a WebSocket {pair} is base + quote.

REST       symbols=btc,eth,xrp    → symbol: "btcusdt", "ethusdt", …
WebSocket  s1.btcusdt.ticker      → pair: base + quote (btcusdt, btctmn, …)

# Builder-DEX markets (s2 only) use a <dex>-<coin> prefix
REST       symbols=xyz-tsla,cash-meta  → symbol: "xyz-tslausdc", "cash-metausdc"
WebSocket  s2.xyz-tslausdc.ticker      → pair: <dex>-<coin><quote>

On s2, eight builder-deployed perp DEXes extend the main perp universe: xyz · flx · vntl · hyna · km · abcd · cash · para. The same coin name can exist on several DEXes, so DEX markets are always addressed with the prefix. Mixed lists (main perp + DEX) are allowed in one request, and every endpoint and channel works identically for both. A hyphen in symbol is the reliable way to tell them apart client-side.

Only currently-listed assets return data. Unknown or delisted symbols fail with symbol not found (REST) or invalid symbol (WebSocket subscribe). The listing refreshes hourly; global top_mover channels are exempt from the check.
Getting started

REST response shape

All REST responses share a common envelope:

200 OK
{
  "data":        /* main payload — array or object */,
  "next_cursor": 1711612800000,  // kline + trade only, when data is non-empty
  "message":     "ok",           // always present; error text on failure
  "success":     true,           // present on success only
  "ts":          1711612800000   // server time, Unix ms
}

On error the response returns HTTP 4xx/5xx and omits "success" — use the HTTP status as the primary error signal; "message" contains the human-readable reason.

Pagination

The kline and trade endpoints return next_cursor for cursor-based pagination — pass it as end_time on the next request to fetch the older page.

next_cursor is the oldest item timestamp in the current page, and is absent when data is empty. /market/detail never returns it — treat that endpoint as a snapshot.

Omitted zero values

Numeric fields equal to zero may be omitted entirely from any REST payload or WebSocket tick. A fresh kline tick with no trades arrives as { symbol, ts, open, high, low, close } with no vol, amount or count. Always treat a missing numeric field as 0.
REST API

GET /market/ticker

GET/rest/v1/market/ticker

24-hour ticker for one or more symbols. Includes open / high / low / close, volume, and best bid/ask.

Parameters

ParamRequiredValuesNotes
sourcerequireds1 · s2Spot feed (s1) or perp feed (s2)
quoterequireds1: usdt · tmn · aed · eur
s2: usdc · tmn
Quote currency
symbolsrequirede.g. btc,eth,xrpComma-separated base symbols (lowercase)

Example request

curl "https://api.cryhub.io/rest/v1/market/ticker?source=s1&quote=usdt&symbols=btc,eth,xrp" \
  -H "Authorization: Bearer YOUR_TOKEN"

Example response

200 OK
{
  "data": [
    {
      "symbol":     "btcusdt",
      "open":       65000.0,
      "high":       67000.0,
      "low":        64500.0,
      "close":      66500.0,
      "last_price": 66500.0,
      "bid":        66490.0,
      "bid_size":   0.5,
      "ask":        66510.0,
      "ask_size":   0.3,
      "last_size":  0.1,
      "vol":        1234.56,
      "amount":     85432000.0,
      "count":      142300
    }
  ],
  "message": "ok",
  "success": true,
  "ts": 1711612800000
}

symbol = base + quote. With quote=tmn and symbols=btc the symbol is btctmn.

REST API

GET /market/detail

GET/rest/v1/market/detail

24-hour market detail — price, change, volume, and percent change derived from kline history.

Parameters

ParamRequiredValuesDefault
sourcerequireds1 · s2
quoterequireds1: usdt · tmn · aed · eur
s2: usdc · tmn
symbolsrequiredbtc,eth
slicerequiredKline period — see reference
sizeoptional1–2000500
start_timeoptionalUnix ms
end_timeoptionalUnix ms

One detail entry is produced per kline candle per symbol, so this endpoint takes the same parameters and the same four query modes as /market/kline. It adds the derived change and change_percent fields.

This endpoint never returns next_cursor — treat it as a snapshot. The most common call is size=1 with slice=1day, which gives the current price and 24-hour stats for each symbol in one request.

Example

# Latest 30 daily candles for BTC and ETH in TMN
curl "https://api.cryhub.io/rest/v1/market/detail?source=s1&quote=tmn&symbols=btc,eth&slice=1day&size=30" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Specific time range
curl "https://api.cryhub.io/rest/v1/market/detail?source=s1&quote=usdt&symbols=btc&slice=1hour&start_time=1711526400000&end_time=1711612800000" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response

200 OK
{
  "data": [
    {
      "symbol":         "btctmn",
      "price":          4056500000.0,
      "change":         91500000.0,
      "high":           4087000000.0,
      "low":            3935500000.0,
      "volume_quote":   5185000000000.0,
      "change_percent": 2.31,
      "volume":         1234.56,
      "count":          142300
    }
  ],
  "message": "ok", "success": true, "ts": 1711612800000
}
REST API

GET /market/kline

GET/rest/v1/market/kline

Candlestick (OHLCV) data for one or more symbols. Supports four query modes and cursor pagination.

Parameters

ParamRequiredValuesDefault
sourcerequireds1 · s2
quoterequireds1: usdt · tmn · aed · eur
s2: usdc · tmn
symbolsrequiredbtc,eth
slicerequired1min · 5min · 15min · 30min · 60min · 4hour · 1day · 1week · 1mon
sizeoptional1–2000500
start_timeoptionalUnix ms, inclusive
end_timeoptionalUnix ms, exclusive
slice=1year is not supported by any REST provider and returns HTTP 400. For yearly candles use the WebSocket channel s1.{pair}.kline.1y s2 has no yearly interval at all.

Query modes

ModeParametersOrderUse case
Asizenewest-firstInitial chart load
Bsize + end_timeoldest-firstScroll left / backward pagination
Csize + start_timeoldest-firstJump to a date, forward window
Dstart_time + end_timeoldest-firstFixed range, server cap 2000
Critical for charts: Mode A returns newest-first. Reverse the array before handing it to a chart library so bars render oldest → newest. Every later page (Mode B) already arrives oldest-first and can be prepended directly. Both sources order identically. Combining size with both start_time and end_time is rejected.

Example

# 60 1-minute BTC/TMN candles
curl "https://api.cryhub.io/rest/v1/market/kline?source=s1&quote=tmn&symbols=btc&slice=1min&size=60" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Older page via cursor (backward pagination)
curl "https://api.cryhub.io/rest/v1/market/kline?source=s1&quote=usdt&symbols=btc&slice=1day&size=100&end_time=1711526400000" \
  -H "Authorization: Bearer YOUR_TOKEN"

# Exact time window
curl "https://api.cryhub.io/rest/v1/market/kline?source=s1&quote=usdt&symbols=btc&slice=1hour&start_time=1711526400000&end_time=1711612800000" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response

200 OK
{
  "data": [
    {
      "symbol": "btcusdt",
      "slice":  "1day",
      "ts":     1711526400000,  // candle timestamp, Unix ms — see note below
      "open":   65000.0,
      "high":   67000.0,
      "low":    64500.0,
      "close":  66500.0,
      "vol":    1234.56,        // quote volume (converted to the quote param)
      "amount": 85432000.0,     // base volume (coin units, not converted)
      "count":  142300
    }
  ],
  "next_cursor": 1711526400000,
  "message": "ok", "success": true, "ts": 1711612800000
}
The timestamp convention differs per source. On s1, ts is the candle open time and is period-aligned. On s2 it is the candle close boundary minus 1 ms. Pagination and bar-matching work the same either way — just use ts consistently as the bar key, and never assume ts % interval === 0 on s2.

With several symbols, all candles arrive in one flat data array — group by symbol before rendering. next_cursor is the global minimum ts across all of them, so reuse the same end_time for every symbol to keep pages aligned. Stop paginating when data.length < size.

REST API

GET /market/depth

GET/rest/v1/market/depth

Order book snapshot for one symbol. Six aggregation levels supported.

Parameters

ParamRequiredValuesDefault
sourcerequireds1 · s2
quoterequireds1: usdt · tmn · aed · eur
s2: usdc · tmn
symbolrequiredbtc
stepoptionalstep0–step5step0
Aggregation levels
step (REST)agg (WebSocket)Notes
step0agg0Raw order book — no aggregation
step1agg1Prices rounded to 1 decimal place
step2agg2Prices rounded to 2 decimal places
step3agg3Prices rounded to the nearest integer
step4agg4Prices rounded to the nearest 10
step5agg5Prices rounded to the nearest 100 — most aggregated

Example

curl "https://api.cryhub.io/rest/v1/market/depth?source=s1&quote=usdt&symbol=btc&step=step0" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response

200 OK
{
  "data": {
    "symbol":  "btcusdt",
    "ts":      1711612800000,
    "version": 123456789,
    "bids": [
      { "price": 66490.0, "qty": 0.5 },
      { "price": 66480.0, "qty": 1.2 }
    ],
    "asks": [
      { "price": 66510.0, "qty": 0.3 },
      { "price": 66520.0, "qty": 0.9 }
    ]
  },
  "message": "ok", "success": true, "ts": 1711612800000
}

bids are price-descending; asks are price-ascending. version is an exchange sequence number — use it to detect a stale snapshot.

REST and WebSocket depth use different shapes. REST returns { price, qty } objects; the WebSocket depth channel returns [price, qty] arrays. Map accordingly when you seed a book from REST and then keep it live over the socket.
REST API

GET /market/trade

GET/rest/v1/market/trade

Recent trades for a symbol. For real-time data use the WebSocket trade channel.

Parameters

ParamRequiredValuesDefault
sourcerequireds1 · s2
quoterequireds1: usdt · tmn · aed · eur
s2: usdc · tmn
symbolrequiredethSingle base symbol — one per request
sizeoptional1–2000500
start_timeoptionalUnix ms, inclusive
end_timeoptionalUnix ms, exclusive
Trade history supports the same four query modes as kline, but time filtering is applied in memory over the most recent ~2000 batches held upstream — cursor pagination is bounded to that window. For continuous history, consume the WebSocket trade channel instead.

Example

curl "https://api.cryhub.io/rest/v1/market/trade?source=s1&quote=aed&symbol=eth&size=50" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response

200 OK
{
  "data": [
    {
      "symbol": "ethaed",
      "id":     987654321,
      "ts":     1711612800000,
      "data": [
        {
          "price":     12300.0,
          "amount":    0.5,
          "direction": "buy",
          "ts":        1711612800000
        }
      ]
    }
  ],
  "next_cursor": 1711612800000,
  "message": "ok", "success": true, "ts": 1711612800000
}

Each batch carries one or more executions in its own data array. direction is "buy" when the taker bought and "sell" when the taker sold. next_cursor is the ts of the oldest batch in the page.

REST API

GET /market/top-mover

GET/rest/v1/market/top-mover

Current top gainers, losers, or highest-volume symbols. Recomputed every ~3 seconds and returned pre-sorted, up to 25 symbols per request.

Parameters

ParamRequiredValuesNotes
sourcerequireds1 · s2Spot feed (s1) or perp feed (s2)
currencyrequireds1: usdt · tmn
s2: usdc · tmn
Not named “quote” on this endpoint
typerequiredgainer · loser · volume
This is the one endpoint that takes currency rather than quote.
typeSort order
gainerDescending by change_percent
loserAscending by change_percent
volumeDescending by quote_volume

Example

curl "https://api.cryhub.io/rest/v1/market/top-mover?source=s1&currency=usdt&type=gainer" \
  -H "Authorization: Bearer YOUR_TOKEN"

Response

200 OK
{
  "data": [
    {
      "symbol":         "btcusdt",
      "price":          79124.07,
      "change":         1580.25,
      "quote_volume":   240482890.81,
      "change_percent": 2.04,
      "volume":         2974.03
    }
  ],
  "message": "ok", "success": true, "ts": 1711612800000
}

On s2 the ranking pools main-perp coins and builder-DEX coins together. Filter client-side on a hyphen in symbol if you need one or the other.

REST API

GET /health

GET/health

Liveness probe at the domain root — note it is not under /rest/v1, where it would 404. Returns HTTP 200 with the plain-text body ok and no JSON envelope.

curl -i https://api.cryhub.io/health
# HTTP/1.1 200 OK
# ok
A 502, 503 or timeout means the API is down, overloaded or unreachable. The probe checks surface reachability only — it does not verify the data pipeline behind it. Suitable for load-balancer probes and uptime monitors.
WebSocket API

WebSocket — connect

URLwss://api.cryhub.io/ws?token=YOUR_32_CHAR_TOKEN

Right after connecting, the server sends a welcome message:

Server → Client (on connect)
{ "msg": "Connected to Cryhub Stream", "ts": 1711612800000 }

Full connection example

const ws = new WebSocket('wss://api.cryhub.io/ws?token=YOUR_TOKEN');

ws.onopen = () => {
  ws.send(JSON.stringify({
    action: 'sub',
    channels: [
      's1.btcusdt.ticker',
      's1.btcusdt.kline.1m',
      's1.btcusdt.depth.agg0',
      's1.usdt.global.top_mover.gainer'
    ]
  }));
};

ws.onmessage = ({ data }) => {
  const msg = JSON.parse(data);

  // Reply to server ping
  if (msg.ping) {
    ws.send(JSON.stringify({ action: 'pong', data: { pong: String(msg.ping) } }));
    return;
  }

  // Market data
  if (msg.ch) {
    console.log(`[${msg.ch}]`, msg.tick, msg.ts);
  }
};

ws.onerror  = (e) => console.error('WS error', e);
ws.onclose  = (e) => console.log('WS closed', e.code);
WebSocket API

WebSocket — protocol

Client → Server

actionDescription
subSubscribe to one or more channels
unsubUnsubscribe
pongReply to server ping — timestamp must be sent as a string
{
  "action": "sub",
  "channels": [
    "s1.btcusdt.ticker",
    "s1.btctmn.ticker",
    "s1.btcusdt.kline.1m",
    "s1.btcusdt.depth.agg0",
    "s1.usdt.global.top_mover.gainer"
  ]
}

Server → Client

FieldDescription
chChannel name — on data and sub-ack messages
tickMarket data — shape varies by channel type
pingNon-zero on heartbeat — must reply with pong
msgSystem text, sub ack, or error message
tsServer time, Unix ms
Heartbeat — Ping/Pong

The server sends a ping every 30 seconds. After 3 unanswered pings the connection is closed.

Server Ping
{ "ping": 1711612830000, "ts": 1711612830000 }
Subscription ack
Sub/Unsub ACK
{ "ch": "s1.btcusdt.ticker,s1.btctmn.ticker", "msg": "sub", "ts": 1711612800000 }
WebSocket API

WebSocket — channels

{source}.{pair}.{stream}[.{arg}]              — per-pair streams
{source}.{currency}.global.top_mover.{type}  — global top movers
ChannelDeliveryDescription
s1.{pair}.tickeralways-on24-hour rolling ticker
s2.{pair}.tickeralways-on24-hour rolling ticker — 0.01% price-delta gate
s1.{pair}.detailalways-on24-hour market detail snapshot
s2.{pair}.detailalways-on24-hour market detail snapshot
s1.{pair}.bboalways-onBest bid/offer
s2.{pair}.bboLazyBest bid/offer — shares its upstream feed with depth
s1.{pair}.tradealways-onExecuted trade batches
s2.{pair}.tradeLazyExecuted trade batches
s1.{pair}.kline.{tf}Lazytf: 1m · 5m · 15m · 30m · 60m · 4h · 1d · 1w · 1mo · 1y
s2.{pair}.kline.{tf}Lazytf: 1m · 5m · 15m · 30m · 60m · 4h · 1d · 1w · 1mo
s1.{pair}.depth.{agg}Lazyagg: agg0–agg5
s2.{pair}.depth.{agg}Lazyagg: agg0–agg5 — all levels share one upstream feed
s1.{currency}.global.top_mover.{type}always-oncurrency: usdt · tmn — type: gainer · loser · volume
s2.{currency}.global.top_mover.{type}always-oncurrency: usdc · tmn — type: gainer · loser · volume
Lazychannels open their upstream connection on the first subscriber and release it after the last unsubscribes, so the first frame can take about 200 ms to arrive. Always-on channels stream continuously.

pair = base + quote: btcusdt · btctmn (s1) · btcusdc · xyz-tslausdc · cash-metausdc (s2)

Only usdt and tmn are published over WebSocket on s1, and only usdc and tmn on s2. AED and EUR pairs — including top_mover — accept a subscription and then stay silent forever. Fetch those over REST.
WebSocket API

WebSocket Ticker

Channels: s1.{pair}.ticker · s2.{pair}.ticker

Message
{
  "ch": "s1.btcusdt.ticker",
  "tick": {
    "symbol":     "btcusdt",
    "open":       65000.0,
    "high":       67000.0,
    "low":        64500.0,
    "close":      66500.0,
    "last_price": 66500.0,
    "bid":        66490.0,
    "bid_size":   0.5,
    "ask":        66510.0,
    "ask_size":   0.3,
    "last_size":  0.1,
    "vol":        1234.56,
    "amount":     85432000.0,
    "count":      142300
  },
  "ts": 1711612800000
}
On s2: OHLC comes from the last candle snapshot and can lag the live mid-price slightly; bid, ask and last_priceare mid-price derived; volume and size fields are omitted when zero; and frames only publish once the price moves 0.01% (roughly a 1–3 second cadence).
WebSocket API

WebSocket Market Detail

Channels: s1.{pair}.detail · s2.{pair}.detail. Field names are identical to REST /market/detail, and high, low, volume_quote and volume are 24-hour values.

Message
{
  "ch": "s1.btcusdt.detail",
  "tick": {
    "symbol":         "btcusdt",
    "price":          66500.0,
    "change":         1500.0,
    "high":           67000.0,
    "low":            64500.0,
    "volume_quote":   85000000.0,
    "change_percent": 2.31,
    "volume":         1234.56
  },
  "ts": 1711612800000
}
WebSocket API

WebSocket BBO

Channels: s1.{pair}.bbo · s2.{pair}.bbo — a lightweight spread indicator that updates whenever the top of the book changes. Use it instead of ticker when the spread is all you need.

Message
{
  "ch": "s1.btcusdt.bbo",
  "tick": {
    "symbol":   "btcusdt",
    "bid":      66490.0,
    "ask":      66510.0,
    "bid_size": 0.5,
    "ask_size": 0.3
  },
  "ts": 1711612800000
}
WebSocket API

WebSocket Kline

Channels: s1.{pair}.kline.{tf} · s2.{pair}.kline.{tf}. Every frame is the current in-progress candle, updated on each trade — use REST /market/kline for closed and historical candles. Lazy channel.

Allowed timeframes: 1m · 5m · 15m · 30m · 60m · 4h · 1d · 1w · 1mo · 1y1y is WebSocket-only and s1-only; s2.*.kline.1y is rejected at subscribe.
Message
{
  "ch": "s1.btcusdt.kline.1m",
  "tick": {
    "symbol": "btcusdt",
    "ts":     1711612800000,
    "open":   66000.0,
    "high":   66800.0,
    "low":    65900.0,
    "close":  66500.0,
    "vol":    12.34,
    "amount": 815481.0,
    "count":  320
  },
  "ts": 1711612800000
}
Bar update logic
if (tick.ts === currentBar.ts) {
  // same candle — update close/high/low/vol/amount/count in place
  updateBar(tick);
} else if (tick.ts > currentBar.ts) {
  // a new candle opened — append it
  appendBar(tick);
}

// tick.ts is in MILLISECONDS. Chart libraries that expect seconds
// (e.g. Lightweight Charts) need tick.ts / 1000.
// vol / amount / count are omitted when zero — default them.
ts matches the REST candle ts for the same source, so it identifies which bar to update — but remember the convention differs: s1 sends the candle open time, s2the close boundary minus 1 ms.
WebSocket API

WebSocket Depth

Channels: s1.{pair}.depth.{agg} · s2.{pair}.depth.{agg}, where agg0agg5 map 1:1 to the REST step0step5 levels. Lazy channel.

Every frame is a full snapshot, not an incremental delta — replace your local order book entirely on each message.
Message
{
  "ch": "s1.btcusdt.depth.agg0",
  "tick": {
    "symbol": "btcusdt",
    "bids": [[66490.0, 0.5], [66480.0, 1.2]],
    "asks": [[66510.0, 0.3], [66520.0, 0.9]]
  },
  "ts": 1711612800000
}

Each entry is a 2-element array: [price, quantity]

WebSocket API

WebSocket Trade

Channels: s1.{pair}.trade · s2.{pair}.trade — executed trades in real time. Prepend incoming trades to the top of your list and trim it to a maximum length to avoid unbounded memory growth.

Message
{
  "ch": "s1.btcusdt.trade",
  "tick": {
    "trades": [
      {
        "symbol":    "btcusdt",
        "price":     66500.0,
        "amount":    0.15,
        "direction": "buy",
        "ts":        1711612800000
      }
    ]
  },
  "ts": 1711612800000
}
WebSocket API

WebSocket Top Mover

Channel format: {source}.{currency}.global.top_mover.{type}

SourceCurrenciesNotes
s1usdt · tmnaed/eur channels exist but never emit
s2usdc · tmnPools main-perp and builder-DEX symbols into one ranking; minimum $10,000 daily volume
typeDescription
gainerTop 25 by 24h % gain
loserTop 25 by 24h % loss
volumeTop 25 by 24h volume
This is the only channel family whose tick is an array and the only one that uses short field names. Every other channel sends long-form keys — do not add short-key fallbacks elsewhere. Lists are recomputed every ~3 seconds.
Message — tick is an array
{
  "ch": "s1.usdt.global.top_mover.gainer",
  "tick": [
    {
      "sym": "shibusdt",
      "p":   0.00002845,    // price
      "c":   0.00000412,    // 24h change
      "qv":  985400000.0,   // 24h quote volume
      "prt": 16.93,         // 24h percent change
      "v":   34600000000.0  // 24h base volume
    }
  ],
  "ts": 1711612800000
}
Reference

Kline periods

REST (slice)WebSocket (timeframe)Description
1min1m1 minute
5min5m5 minutes
15min15m15 minutes
30min30m30 minutes
60min60m1 hour
4hour4h4 hours
1day1dDaily
1week1wWeekly
1mon1moMonthly
1yYearly — WebSocket only, and s1 only
There is no REST equivalent of 1y: slice=1year returns HTTP 400 on both sources, and s2.*.kline.1y is rejected at subscribe time.
Reference

Supported currencies

CodeNameSourcesAvailability
usdtTethers1REST + WebSocket — native, no conversion
usdcUSD Coins2REST + WebSocket — native, no conversion
tmnIranian Tomans1 · s2REST + WebSocket — converted server-side
aedUAE Dirhams1REST only — never published over WebSocket
eurEuros1REST only — never published over WebSocket
Fiat quotes are converted server-side from the native quote, with rates refreshed every 4 seconds. Prices arrive already converted, so no client-side maths is needed. If the rate feed drops, the most recent valid rate keeps being applied.
Reference

Errors

HTTPmessageCause
400source X is not supportedInvalid source — use s1 or s2
400symbols is requiredMissing symbols param
400quote is requiredMissing quote param
400quote not supported for this sourcee.g. aed on s2
400invalid slice for this provider: "1year"Slice not available on this source
400invalid size: N, accepted range 1 to 2000size out of range
400cannot combine size with both start_time and end_timeInvalid param combination
400start_time must be earlier than end_timeBad time window
400invalid step "X", accepted: step0..step5Invalid step
400type "X" is not supported; valid values: gainer, loser, volumeInvalid top-mover type
401unauthorized(invalid token)Invalid or malformed token
401unauthorized(token required)Token missing
500symbol not found: xyzUnknown or delisted symbol
500failed to fetch market dataUpstream or server error

Error response example

Error responses return HTTP 4xx/5xx and omit "success". Use the HTTP status code as the primary signal.

400 Bad Request
{
  "message": "start_time must be earlier than end_time",
  "ts":      1711612800000
}

WebSocket error messages

Socket errors arrive as a plain msg frame with no ch.

Server Error Messages
{ "msg": "invalid action: foo",     "ts": 1711612800000 }
{ "msg": "channels are empty",      "ts": 1711612800000 }
{ "msg": "invalid source: s9",      "ts": 1711612800000 }
{ "msg": "invalid symbol: foousdt", "ts": 1711612800000 }
{ "msg": "failed to sub: ...",      "ts": 1711612800000 }
{ "msg": "ping timeout. connection closed.", "ts": 1711612800000 }